embedded_layout/
lib.rs

1//! Enable simple layout operations in [`embedded-graphics`]
2//!
3//! This crate extends [`embedded-graphics`] with tools that ease positioning of drawable objects.
4//!
5//! `embedded-layout` consists of three main parts:
6//! - [alignments] that can be used to position two objects relative to one another
7//!   * `horizontal`
8//!     * `NoAlignment`, `Left`, `Right`, `Center`
9//!     * `LeftToRight`, `RightToLeft`
10//!   * `vertical`
11//!     * `NoAlignment`, `Top`, `Bottom`, `Center`
12//!     * `TopToBottom`, `BottomToTop`
13//! - [layouts] that can be used to arrange multiple views
14//!   * `LinearLayout`
15//! - [view groups] which are collections of view objects
16//!   * `Chain` to create ad-hoc collections (can hold views of different types)
17//!   * `Views` to create view groups from arrays and slices (can only hold views of a single type)
18//!   * `derive(ViewGroup)` to turn any plain old Rust struct into a view group
19//!
20//! # Views
21//!
22//! The term "view" refers to anything `embedded-layout` can work with. Basically, a view is an
23//! object that can be displayed. [`View`] is the most basic trait in `embedded-layout`. Views
24//! implement the [`View`] trait to enable translation and alignment operations on them, and also to
25//! allow them to be used with layouts.
26//!
27//! [`View`] is implemented for [`embedded-graphics`] display objects. There's also an example about
28//! how you can implement custom [`View`] objects.
29//!
30//! ## Examples
31//!
32//! The examples are based on [the `embedded-graphics` simulator]. The simulator is built on top of
33//! `SDL2`. See the [simulator README] for more information.
34//!
35//! ### Draw some text to the center of the display
36//!
37//! ```
38//! # use embedded_graphics::mock_display::MockDisplay;
39//! # let mut display: MockDisplay<BinaryColor> = MockDisplay::new();
40//! #
41//! use embedded_graphics::{
42//!     mono_font::{ascii::FONT_6X9, MonoTextStyle},
43//!     pixelcolor::BinaryColor,
44//!     prelude::*,
45//!     text::Text,
46//! };
47//! use embedded_layout::prelude::*;
48//!
49//! // Create a Rectangle from the display's dimensions
50//! let display_area = display.bounding_box();
51//!
52//! let text_style = MonoTextStyle::new(&FONT_6X9, BinaryColor::On);
53//!
54//! Text::new("Hello!", Point::zero(), text_style)
55//!     // align text to the center of the display
56//!     .align_to(&display_area, horizontal::Center, vertical::Center)
57//!     .draw(&mut display)
58//!     .unwrap();
59//! ```
60//!
61//! ### Use [`LinearLayout`] to arrange multiple objects
62//!
63//! ```
64//! # use embedded_graphics::mock_display::MockDisplay;
65//! # let mut display: MockDisplay<BinaryColor> = MockDisplay::new();
66//! #
67//! use embedded_graphics::{
68//!     mono_font::{ascii::FONT_6X9, MonoTextStyle},
69//!     pixelcolor::BinaryColor,
70//!     prelude::*,
71//!     text::Text,
72//! };
73//! use embedded_layout::{layout::linear::LinearLayout, prelude::*};
74//!
75//! let display_area = display.bounding_box();
76//!
77//! let text_style = MonoTextStyle::new(&FONT_6X9, BinaryColor::On);
78//!
79//! LinearLayout::vertical(
80//!     Chain::new(Text::new("Vertical", Point::zero(), text_style))
81//!         .append(Text::new("Linear", Point::zero(), text_style))
82//!         .append(Text::new("Layout", Point::zero(), text_style))
83//! )
84//! .with_alignment(horizontal::Center)
85//! .arrange()
86//! .align_to(&display_area, horizontal::Center, vertical::Center)
87//! .draw(&mut display)
88//! .unwrap();
89//! ```
90//!
91//! [`embedded-graphics`]: https://crates.io/crates/embedded-graphics/0.6.2
92//! [the `embedded-graphics` simulator]: https://crates.io/crates/embedded-graphics-simulator/0.2.1
93//! [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name
94//! [`View`]: crate::View
95//! [layouts]: crate::layout
96//! [`LinearLayout`]: crate::layout::linear::LinearLayout
97//! [simulator README]: https://github.com/jamwaffles/embedded-graphics/tree/v0.6/simulator#usage-without-sdl2
98//! [alignments]: crate::align
99//! [view groups]: crate::view_group
100
101#![cfg_attr(not(test), no_std)]
102#![deny(missing_docs)]
103#![deny(clippy::missing_inline_in_public_items)]
104#![warn(clippy::all)]
105
106use embedded_graphics::{geometry::Point, prelude::*, primitives::Rectangle};
107
108pub use embedded_layout_macros::ViewGroup;
109
110pub mod align;
111pub mod layout;
112pub mod object_chain;
113pub mod utils;
114pub mod view_group;
115
116/// The essentials. Also contains most of `embedded-graphics'` prelude.
117pub mod prelude {
118    pub use crate::{
119        align::{horizontal, vertical, Align},
120        chain,
121        object_chain::{Chain, Link},
122        utils::rect_helper::RectExt,
123        view_group::Views,
124        View,
125    };
126}
127
128/// A `View` is the base unit for most of the `embedded-layout` operations.
129///
130/// `View`s must have a size and a position.
131///
132/// See the `custom_view` example for how you can define more complex views.
133pub trait View {
134    /// Get the size of a View.
135    #[inline]
136    fn size(&self) -> Size {
137        self.bounds().size
138    }
139
140    /// Object-safe version of `translate_mut()`.
141    ///
142    /// The default implementations of `translate` and `translate_mut` both call this functions.
143    fn translate_impl(&mut self, by: Point);
144
145    /// Move the origin of an object by a given number of (x, y) pixels, mutating the object in place.
146    ///
147    /// If you a looking for a method to implement, you might want `translate_impl()` instead.
148    #[inline]
149    fn translate_mut(&mut self, by: Point) -> &mut Self
150    where
151        Self: Sized,
152    {
153        self.translate_impl(by);
154        self
155    }
156
157    /// Move the origin of an object by a given number of (x, y) pixels, returning a new object.
158    ///
159    /// If you a looking for a method to implement, you might want `translate_impl()` instead.
160    #[inline]
161    fn translate(mut self, by: Point) -> Self
162    where
163        Self: Sized,
164    {
165        self.translate_impl(by);
166        self
167    }
168
169    /// Returns the bounding box of the `View` as a `Rectangle`
170    fn bounds(&self) -> Rectangle;
171}
172
173impl<T> View for T
174where
175    T: Transform + Dimensions,
176{
177    #[inline]
178    fn translate_impl(&mut self, by: Point) {
179        Transform::translate_mut(self, by);
180    }
181
182    #[inline]
183    fn bounds(&self) -> Rectangle {
184        self.bounding_box()
185    }
186}
187
188#[cfg(test)]
189mod test {
190    use crate::prelude::*;
191
192    #[allow(dead_code)]
193    fn view_is_object_safe(_: &dyn View) {}
194}