Compare commits
2 Commits
256341cd5e
...
a3cf0ec08c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3cf0ec08c | ||
|
|
8e5926eba8 |
1
gui
1
gui
@ -1 +0,0 @@
|
|||||||
Subproject commit 12afc17f90756988b9011240e6884fa0e3fb48ef
|
|
||||||
1
gui/.gitignore
vendored
Normal file
1
gui/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
9
gui/Cargo.toml
Normal file
9
gui/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "gui"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embedded-graphics = "0.8.1"
|
||||||
|
embedded-layout = "0.4.1"
|
||||||
|
profont = "0.7.0"
|
||||||
158
gui/src/lib.rs
Normal file
158
gui/src/lib.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use embedded_graphics::{pixelcolor::Rgb565, prelude::*};
|
||||||
|
|
||||||
|
mod widgets;
|
||||||
|
|
||||||
|
use core::cmp::PartialEq;
|
||||||
|
use core::result::Result::{self, Ok};
|
||||||
|
|
||||||
|
pub type Color = Rgb565;
|
||||||
|
|
||||||
|
/// Enum representing the valid actions that can be performed in the GUI.
|
||||||
|
/// Each variant corresponds to a user input action that the interface can respond to.
|
||||||
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
|
pub enum GuiAction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Select,
|
||||||
|
Back,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enum representing all available views in the GUI.
|
||||||
|
/// Each variant corresponds to a different screen or state within the application.
|
||||||
|
mod views;
|
||||||
|
pub enum GuiView {
|
||||||
|
MainMenu(views::main_menu::State),
|
||||||
|
Scan,
|
||||||
|
Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GuiView {
|
||||||
|
/// Draws the current view to the provided display target.
|
||||||
|
/// This method maps each individual view's drawing function to a common interface,
|
||||||
|
/// allowing for a unified way to render different views.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `display`: A mutable reference to the display target where the view will be drawn.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Result<(), D::Error>`: Returns Ok if drawing was successful, or an error if it failed.
|
||||||
|
pub fn draw<D>(&self, display: &mut D) -> Result<(), D::Error>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = Color>,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
Self::MainMenu(state) => views::main_menu::draw(state, display)?,
|
||||||
|
Self::Scan => views::scan::draw(display)?,
|
||||||
|
Self::Settings => views::settings::draw(display)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles user actions based on the current view.
|
||||||
|
/// This method maps individual action handling functions to a common interface,
|
||||||
|
/// allowing the GUI to respond to user inputs appropriately depending on the current view.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `action`: The action that has occurred, represented by the `GuiAction` enum.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Self`: Returns the updated view after processing the action.
|
||||||
|
/// The state may change based on the action taken, particularly in the MainMenu.
|
||||||
|
pub fn action(self, action: GuiAction) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::MainMenu(state) => views::main_menu::action(state, action),
|
||||||
|
Self::Scan => Self::Scan,
|
||||||
|
Self::Settings => Self::Settings,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A struct representing the graphical user interface (GUI).
|
||||||
|
/// It holds the current display target and the active view of the GUI.
|
||||||
|
pub struct Gui<D>
|
||||||
|
where
|
||||||
|
// The display must implement the DrawTarget trait with a specified Color type
|
||||||
|
D: DrawTarget<Color = Color>,
|
||||||
|
{
|
||||||
|
/// The display target where the GUI will be rendered
|
||||||
|
display: D,
|
||||||
|
/// The current view of the GUI, represented by the GuiView enum
|
||||||
|
view: GuiView,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> Gui<D>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = Color>,
|
||||||
|
{
|
||||||
|
/// Creates a new instance of the GUI with the specified display target.
|
||||||
|
/// Initializes the GUI to start in the Main Menu view with the first item selected.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `display`: The display target to be used for rendering the GUI.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Self`: A new instance of the Gui struct.
|
||||||
|
pub fn new(display: D) -> Self {
|
||||||
|
Self {
|
||||||
|
view: GuiView::MainMenu(views::main_menu::State { selected: 0 }), // Init on main menu
|
||||||
|
display,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes a user action and updates the GUI state accordingly.
|
||||||
|
/// This method delegates the action handling to the current view's action method.
|
||||||
|
///
|
||||||
|
/// # Parameters
|
||||||
|
/// - `gui_action`: The action that has occurred, represented by the GuiAction enum.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Self`: A new instance of the Gui struct with the updated view.
|
||||||
|
pub fn action(self, gui_action: GuiAction) -> Self {
|
||||||
|
// Get the resulting view after the action has been performed
|
||||||
|
let new_view = self.view.action(gui_action);
|
||||||
|
|
||||||
|
// Create a new GUI with the new view, retaining the display
|
||||||
|
Self {
|
||||||
|
display: self.display, // Move display to new GUI struct
|
||||||
|
view: new_view, // Set the new view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills the entire display with a solid black color.
|
||||||
|
/// This can be used to clear the screen before drawing the next frame.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Result<(), D::Error>`: Returns Ok if the fill operation was successful, or an error if it failed.
|
||||||
|
pub fn fill_black(&mut self) -> Result<(), D::Error> {
|
||||||
|
let display: &mut D = &mut self.display;
|
||||||
|
display.fill_solid(&display.bounding_box(), Color::BLACK)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws the current view of the GUI onto the display.
|
||||||
|
/// This method calls the draw method of the current view, rendering it to the display target.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `Result<(), D::Error>`: Returns Ok if the drawing operation was successful, or an error if it failed.
|
||||||
|
pub fn draw(&mut self) -> Result<(), D::Error> {
|
||||||
|
let display: &mut D = &mut self.display; // Get a mutable reference to the display
|
||||||
|
self.view.draw(display)?; // Draw the view
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the display target.
|
||||||
|
/// This can be useful for accessing the display outside of the GUI struct.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// - `&D`: A reference to the display target.
|
||||||
|
pub fn display_ref(&self) -> &D {
|
||||||
|
&self.display // Return a reference to the display
|
||||||
|
}
|
||||||
|
}
|
||||||
72
gui/src/views/main_menu.rs
Normal file
72
gui/src/views/main_menu.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use crate::widgets::{button::Button, header::Header};
|
||||||
|
use crate::{Color, GuiAction, GuiView};
|
||||||
|
use core::result::Result::{self, Ok};
|
||||||
|
use embedded_graphics::prelude::*;
|
||||||
|
use embedded_layout::{
|
||||||
|
layout::linear::{FixedMargin, LinearLayout},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn create_button(button_index: u8, selected_index: u8, text: &str) -> Button {
|
||||||
|
let button_width = 240;
|
||||||
|
let button_height = 60;
|
||||||
|
|
||||||
|
if button_index == selected_index {
|
||||||
|
Button::new(Point::zero(), Size::new(button_width, button_height), text).toggle_highlight()
|
||||||
|
} else {
|
||||||
|
Button::new(Point::zero(), Size::new(button_width, button_height), text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw<D>(state: &State, display: &mut D) -> Result<(), D::Error>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = Color>,
|
||||||
|
{
|
||||||
|
// Create the header
|
||||||
|
let header = Header::new("Main Menu");
|
||||||
|
header.draw(display)?;
|
||||||
|
|
||||||
|
// Create the buttons
|
||||||
|
let scan_button = create_button(0, state.selected, "Scan");
|
||||||
|
let settings_button = create_button(1, state.selected, "Settings");
|
||||||
|
|
||||||
|
// Draw the buttons in a vertical layout
|
||||||
|
LinearLayout::vertical(Chain::new(scan_button).append(settings_button))
|
||||||
|
.with_spacing(FixedMargin(10))
|
||||||
|
.arrange()
|
||||||
|
.align_to(
|
||||||
|
&display.bounding_box(),
|
||||||
|
horizontal::Center,
|
||||||
|
vertical::Center,
|
||||||
|
)
|
||||||
|
.draw(display)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct State {
|
||||||
|
/// Which button is currently highlighted
|
||||||
|
pub selected: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn action(state: State, action: GuiAction) -> GuiView {
|
||||||
|
let num_buttons: u8 = 2;
|
||||||
|
|
||||||
|
let new_selected = match (action, state.selected) {
|
||||||
|
// Check if a button was "pressed"
|
||||||
|
(GuiAction::Select, 0) => return GuiView::Scan,
|
||||||
|
(GuiAction::Select, 1) => return GuiView::Settings,
|
||||||
|
|
||||||
|
// Scroll the selected and output the new selected
|
||||||
|
(GuiAction::Up, 0) => num_buttons - 1,
|
||||||
|
(GuiAction::Up, _) => state.selected - 1,
|
||||||
|
(GuiAction::Down, i) if i == num_buttons - 1 => 0,
|
||||||
|
(GuiAction::Down, _) => state.selected + 1,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
GuiView::MainMenu(State {
|
||||||
|
selected: new_selected,
|
||||||
|
})
|
||||||
|
}
|
||||||
4
gui/src/views/mod.rs
Normal file
4
gui/src/views/mod.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub mod main_menu;
|
||||||
|
pub mod scan;
|
||||||
|
pub mod settings;
|
||||||
|
pub mod void;
|
||||||
12
gui/src/views/scan.rs
Normal file
12
gui/src/views/scan.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use crate::widgets::header::Header;
|
||||||
|
use core::result::Result::{self, Ok};
|
||||||
|
use embedded_graphics::prelude::*;
|
||||||
|
|
||||||
|
pub fn draw<D>(display: &mut D) -> Result<(), D::Error>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = crate::Color>,
|
||||||
|
{
|
||||||
|
Header::new("Scan page").draw(display)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
12
gui/src/views/settings.rs
Normal file
12
gui/src/views/settings.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use crate::widgets::header::Header;
|
||||||
|
use core::result::Result::{self, Ok};
|
||||||
|
use embedded_graphics::prelude::*;
|
||||||
|
|
||||||
|
pub fn draw<D>(display: &mut D) -> Result<(), D::Error>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = crate::Color>,
|
||||||
|
{
|
||||||
|
Header::new("Settings page").draw(display)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
27
gui/src/views/void.rs
Normal file
27
gui/src/views/void.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use crate::widgets::button::Button;
|
||||||
|
use core::result::Result::{self, Ok};
|
||||||
|
use embedded_graphics::prelude::*;
|
||||||
|
use embedded_layout::{
|
||||||
|
layout::linear::{FixedMargin, LinearLayout},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn draw<D>(display: &mut D) -> Result<(), D::Error>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = crate::Color>,
|
||||||
|
{
|
||||||
|
let button1 = Button::new(Point::zero(), Size::new(100, 50), "Void");
|
||||||
|
let button2 = Button::new(Point::zero(), Size::new(100, 50), "Void");
|
||||||
|
|
||||||
|
LinearLayout::vertical(Chain::new(button1).append(button2))
|
||||||
|
.with_spacing(FixedMargin(10))
|
||||||
|
.arrange()
|
||||||
|
.align_to(
|
||||||
|
&display.bounding_box(),
|
||||||
|
horizontal::Center,
|
||||||
|
vertical::Center,
|
||||||
|
)
|
||||||
|
.draw(display)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
90
gui/src/widgets/button.rs
Normal file
90
gui/src/widgets/button.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
use core::result::Result::{self, Ok};
|
||||||
|
use embedded_graphics::{
|
||||||
|
mono_font::{ascii::FONT_10X20, MonoTextStyle},
|
||||||
|
pixelcolor::Rgb888,
|
||||||
|
prelude::*,
|
||||||
|
primitives::{PrimitiveStyle, Rectangle},
|
||||||
|
text::Text,
|
||||||
|
};
|
||||||
|
use embedded_layout::{
|
||||||
|
align::{horizontal, vertical, Align},
|
||||||
|
View,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Button<'a> {
|
||||||
|
bounds: Rectangle,
|
||||||
|
pub highlighted: bool,
|
||||||
|
pub text: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Button<'a> {
|
||||||
|
pub fn new(position: Point, size: Size, text: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
bounds: Rectangle::new(position, size),
|
||||||
|
highlighted: false,
|
||||||
|
text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_highlight(self) -> Self {
|
||||||
|
Self {
|
||||||
|
bounds: self.bounds,
|
||||||
|
highlighted: !self.highlighted,
|
||||||
|
text: self.text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for Button<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn translate_impl(&mut self, by: Point) {
|
||||||
|
View::translate_mut(&mut self.bounds, by);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bounds(&self) -> Rectangle {
|
||||||
|
self.bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drawable for Button<'_> {
|
||||||
|
type Color = crate::Color;
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = Self::Color>,
|
||||||
|
{
|
||||||
|
// Create styles
|
||||||
|
let border_style = PrimitiveStyle::with_stroke(Self::Color::WHITE, 5);
|
||||||
|
|
||||||
|
let normal_background_style = PrimitiveStyle::with_fill(Self::Color::CSS_GRAY);
|
||||||
|
let highlighted_background_style = PrimitiveStyle::with_fill(Self::Color::BLUE);
|
||||||
|
|
||||||
|
let background_style = if self.highlighted {
|
||||||
|
highlighted_background_style
|
||||||
|
} else {
|
||||||
|
normal_background_style
|
||||||
|
};
|
||||||
|
|
||||||
|
let character_style = MonoTextStyle::new(&FONT_10X20, Self::Color::WHITE);
|
||||||
|
|
||||||
|
// Create the border
|
||||||
|
let border = self.bounds.into_styled(border_style);
|
||||||
|
|
||||||
|
// Create the button fill
|
||||||
|
let fill = Rectangle::new(Point::zero(), self.bounds.size()).into_styled(background_style);
|
||||||
|
let fill = fill.align_to(&border, horizontal::Left, vertical::Center);
|
||||||
|
|
||||||
|
// Create the button text
|
||||||
|
let text = Text::new(self.text, Point::zero(), character_style);
|
||||||
|
let text = text.align_to(&border, horizontal::Center, vertical::Center);
|
||||||
|
|
||||||
|
// Draw the views
|
||||||
|
fill.draw(target)?;
|
||||||
|
border.draw(target)?;
|
||||||
|
text.draw(target)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
68
gui/src/widgets/header.rs
Normal file
68
gui/src/widgets/header.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use core::result::Result::{self, Ok};
|
||||||
|
use embedded_graphics::{
|
||||||
|
mono_font::MonoTextStyle,
|
||||||
|
prelude::*,
|
||||||
|
primitives::{PrimitiveStyle, Rectangle},
|
||||||
|
text::Text,
|
||||||
|
};
|
||||||
|
use embedded_layout::{
|
||||||
|
align::{horizontal, vertical, Align},
|
||||||
|
View,
|
||||||
|
};
|
||||||
|
use profont::PROFONT_24_POINT;
|
||||||
|
|
||||||
|
use crate::Color;
|
||||||
|
|
||||||
|
pub struct Header<'a> {
|
||||||
|
bounds: Rectangle,
|
||||||
|
pub text: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Header<'a> {
|
||||||
|
pub fn new(text: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
bounds: Rectangle::new(Point::zero(), Size::new(240, 30)),
|
||||||
|
text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for Header<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn translate_impl(&mut self, by: Point) {
|
||||||
|
View::translate_mut(&mut self.bounds, by);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bounds(&self) -> Rectangle {
|
||||||
|
self.bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drawable for Header<'_> {
|
||||||
|
type Color = crate::Color;
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = crate::Color>,
|
||||||
|
{
|
||||||
|
// Create styles
|
||||||
|
let background_style = PrimitiveStyle::with_fill(Color::CSS_MINT_CREAM);
|
||||||
|
let character_style = MonoTextStyle::new(&PROFONT_24_POINT, Self::Color::BLACK);
|
||||||
|
|
||||||
|
// Create the background
|
||||||
|
let background =
|
||||||
|
Rectangle::new(Point::zero(), self.bounds.size()).into_styled(background_style);
|
||||||
|
|
||||||
|
// Create the text
|
||||||
|
let text = Text::new(self.text, Point::zero(), character_style);
|
||||||
|
let text = text.align_to(&background, horizontal::Center, vertical::Center);
|
||||||
|
|
||||||
|
// Draw the menu
|
||||||
|
background.draw(target)?;
|
||||||
|
text.draw(target)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
3
gui/src/widgets/mod.rs
Normal file
3
gui/src/widgets/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod button;
|
||||||
|
pub mod header;
|
||||||
|
pub mod progress;
|
||||||
95
gui/src/widgets/progress.rs
Normal file
95
gui/src/widgets/progress.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
use core::result::Result::{self, Ok};
|
||||||
|
use embedded_graphics::{
|
||||||
|
geometry::{Point, Size},
|
||||||
|
pixelcolor::Rgb888,
|
||||||
|
prelude::*,
|
||||||
|
primitives::{PrimitiveStyle, Rectangle},
|
||||||
|
};
|
||||||
|
use embedded_layout::{
|
||||||
|
align::{horizontal, vertical, Align},
|
||||||
|
View,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ProgressBar {
|
||||||
|
progress: u32,
|
||||||
|
bounds: Rectangle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgressBar {
|
||||||
|
/// The progress bar needs a configurable position and size
|
||||||
|
pub fn new(position: Point, size: Size) -> Self {
|
||||||
|
Self {
|
||||||
|
bounds: Rectangle::new(position, size),
|
||||||
|
progress: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The progress bar needs a configurable position and size
|
||||||
|
pub fn with_progress(self, progress: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
bounds: self.bounds,
|
||||||
|
progress,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn increment(self) -> Self {
|
||||||
|
Self {
|
||||||
|
bounds: self.bounds,
|
||||||
|
progress: self.progress + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Impleenting `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) {
|
||||||
|
// NB: Do not use translate (non-mut)
|
||||||
|
View::translate_mut(&mut self.bounds, by);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn bounds(&self) -> Rectangle {
|
||||||
|
self.bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We need to implement `Drawable` for a reference of our view
|
||||||
|
impl Drawable for ProgressBar {
|
||||||
|
type Color = Rgb888;
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
|
||||||
|
where
|
||||||
|
D: DrawTarget<Color = Self::Color>,
|
||||||
|
{
|
||||||
|
// Create styles
|
||||||
|
let border_style = PrimitiveStyle::with_stroke(Self::Color::WHITE, 5);
|
||||||
|
let progress_style = PrimitiveStyle::with_fill(Self::Color::RED);
|
||||||
|
|
||||||
|
// Create a border
|
||||||
|
let border = self.bounds.into_styled(border_style);
|
||||||
|
|
||||||
|
// Create a rectangle to indicate progress
|
||||||
|
let progress = Rectangle::new(
|
||||||
|
Point::zero(),
|
||||||
|
Size::new(
|
||||||
|
(self.bounds.size().width - 4) * self.progress / 100,
|
||||||
|
self.bounds.size().height,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_styled(progress_style);
|
||||||
|
|
||||||
|
// Align the progress bar with the border
|
||||||
|
let progress = progress
|
||||||
|
.align_to(&border, horizontal::Left, vertical::Center)
|
||||||
|
.translate(Point::new(6, 0));
|
||||||
|
|
||||||
|
// Draw the views
|
||||||
|
progress.draw(target)?;
|
||||||
|
border.draw(target)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user