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}