1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
// Copyright (C) 2018-2024 Daniel Mueller (deso@posteo.net)
// SPDX-License-Identifier: GPL-3.0-or-later
use crate::Cap;
use crate::Renderable;
/// A bounding box representing the area that a widget may occupy. A
/// bounding box always describes a rectangular area. The origin [x=0,
/// y=0] is typically assumed to reside in the upper left corner of the
/// screen, but it is really up to the individual [`Renderer`] to make
/// do with whatever is provided.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct BBox {
/// The x-coordinate of the bounding box.
pub x: u16,
/// The y-coordinate of the bounding box.
pub y: u16,
/// The width of the bounding box.
pub w: u16,
/// The height of the bounding box.
pub h: u16,
}
/// An abstraction for objects used for rendering widgets.
pub trait Renderer {
/// Retrieve the bounding box of the renderable area (typically the
/// screen).
/// Note that the units to be used are not specified. That is, the
/// result could be in pixels, characters (in case of a terminal), or
/// just arbitrary numbers (if virtual coordinates are being used), as
/// long as this `Renderer` knows how to interpret them.
fn renderable_area(&self) -> BBox;
/// Perform some pre-render step.
fn pre_render(&self) {}
/// Render an object.
///
/// Objects are represented as [`Renderable`] and need to be cast into
/// the actual widget type to render by the `Renderer` itself, should
/// that be necessary. A simplified implementation could look as
/// follows:
/// ```rust
/// # use gui::{BBox, Cap, Id, Renderer, Renderable};
/// # use gui::derive::{Handleable, Widget};
/// # #[derive(Debug, Widget, Handleable)]
/// # #[gui(Event = ())]
/// # struct ConcreteWidget1 {
/// # id: Id,
/// # }
/// # #[derive(Debug, Widget, Handleable)]
/// # #[gui(Event = ())]
/// # struct ConcreteWidget2 {
/// # id: Id,
/// # }
/// # #[derive(Debug)]
/// # struct TestRenderer {}
/// # impl TestRenderer {
/// # fn render_concrete_widget1(&self, widget: &ConcreteWidget1, bbox: BBox) -> BBox {
/// # bbox
/// # }
/// # fn render_concrete_widget2(&self, widget: &ConcreteWidget1, bbox: BBox) -> BBox {
/// # bbox
/// # }
/// # }
/// # impl Renderer for TestRenderer {
/// # fn renderable_area(&self) -> BBox {
/// # Default::default()
/// # }
/// fn render(&self, widget: &dyn Renderable, cap: &dyn Cap, bbox: BBox) -> BBox {
/// if let Some(widget1) = widget.downcast_ref::<ConcreteWidget1>() {
/// self.render_concrete_widget1(widget1, bbox)
/// } else if let Some(widget2) = widget.downcast_ref::<ConcreteWidget1>() {
/// self.render_concrete_widget2(widget2, bbox)
/// } else {
/// panic!("Renderable {:?} is unknown to the renderer", widget)
/// }
/// }
/// # }
/// # fn main() {}
/// ```
// TODO: Ideally we would like to have a double dispatch mechanism for
// determining the object to render.
fn render(&self, object: &dyn Renderable, cap: &dyn Cap, bbox: BBox) -> BBox;
/// A method invoked once rendering of a widget and all its children
/// concluded.
#[allow(unused_variables)]
fn render_done(&self, object: &dyn Renderable, cap: &dyn Cap, bbox: BBox) {}
/// Perform some post-render step.
fn post_render(&self) {}
}