//! # Example: Custom View - Progress bar
//!
//! 
//!
//! This example shows what's necessary to implement a reusable View.
use embedded_graphics_simulator::{
BinaryColorTheme, OutputSettingsBuilder, SimulatorDisplay, Window,
};
use embedded_graphics::{
draw_target::DrawTarget,
pixelcolor::BinaryColor,
prelude::{Dimensions, Point, Primitive, Size},
primitives::{PrimitiveStyle, Rectangle},
Drawable,
};
use embedded_layout::{
layout::linear::{spacing::FixedMargin, LinearLayout},
prelude::*,
};
pub struct ProgressBar {
progress: u32,
bounds: Rectangle,
}
impl ProgressBar {
/// The progress bar has a configurable position and size
fn new(position: Point, size: Size) -> Self {
Self {
bounds: Rectangle::new(position, size),
progress: 0,
}
}
fn with_progress(self, progress: u32) -> Self {
Self {
bounds: self.bounds,
progress,
}
}
}
/// Implementing `View` is required by the layout and alignment operations
/// `View` teaches `embedded-layout` where our object is, how big it is and how to move it.
impl View for ProgressBar {
#[inline]
fn translate_impl(&mut self, by: Point) {
// make sure you don't accidentally call `translate`!
self.bounds.translate_mut(by);
}
#[inline]
fn bounds(&self) -> Rectangle {
self.bounds
}
}
/// Need to implement `Drawable` for a _reference_ of our view
impl Drawable for ProgressBar {
type Color = BinaryColor;
type Output = ();
fn draw<D: DrawTarget<Color = BinaryColor>>(&self, display: &mut D) -> Result<(), D::Error> {
// Create styles
let border_style = PrimitiveStyle::with_stroke(BinaryColor::On, 1);
let progress_style = PrimitiveStyle::with_fill(BinaryColor::On);
// Create a 1px border
let border = self.bounds.into_styled(border_style);
// Create a rectangle that will indicate progress
let progress = Rectangle::new(
Point::zero(),
// sizes are calculated so that the rectangle will have a 1px margin
Size::new(
(self.bounds.size().width - 4) * self.progress / 100,
self.bounds.size().height - 4,
),
)
.into_styled(progress_style);
// Align progress bar within border
let progress = progress
.align_to(&border, horizontal::Left, vertical::Center)
.translate(Point::new(2, 0));
// Draw views
border.draw(display)?;
progress.draw(display)?;
Ok(())
}
}
fn main() -> Result<(), core::convert::Infallible> {
let mut display: SimulatorDisplay<BinaryColor> = SimulatorDisplay::new(Size::new(128, 64));
let output_settings = OutputSettingsBuilder::new()
.theme(BinaryColorTheme::OledBlue)
.build();
// Create a Rectangle from the display's dimensions
let display_area = display.bounding_box();
// Two bigger progress bars
let progress1 = ProgressBar::new(Point::zero(), Size::new(64, 8)).with_progress(10);
let progress2 = ProgressBar::new(Point::zero(), Size::new(64, 8)).with_progress(50);
// Two smaller progress bars
let progress3 = ProgressBar::new(Point::zero(), Size::new(32, 6)).with_progress(50);
let progress4 = ProgressBar::new(Point::zero(), Size::new(32, 6)).with_progress(100);
// Arrange on display and draw
LinearLayout::vertical(
Chain::new(progress1)
.append(progress2)
.append(progress3)
.append(progress4),
)
.with_spacing(FixedMargin(4))
.arrange()
.align_to(&display_area, horizontal::Center, vertical::Center)
.draw(&mut display)
.unwrap();
Window::new("Custom View example", &output_settings).show_static(&display);
Ok(())
}