watchface/
textual_time_watchface.rs

1/* Copyright (C) 2020 Casper Meijn <casper@meijn.net>
2 * SPDX-License-Identifier: GPL-3.0-or-later
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18use crate::battery::ChargerState;
19use crate::styled::Styled;
20use crate::time::Time;
21use crate::Watchface;
22use core::fmt::Write;
23use core::marker::PhantomData;
24
25use embedded_graphics::draw_target::DrawTarget;
26use embedded_graphics::geometry::Point;
27use embedded_graphics::mono_font::{
28    ascii::{FONT_10X20, FONT_9X15_BOLD},
29    MonoTextStyleBuilder,
30};
31use embedded_graphics::pixelcolor::RgbColor;
32use embedded_graphics::text::Text;
33use embedded_graphics::Drawable;
34use embedded_layout::layout::linear::LinearLayout;
35use embedded_layout::prelude::*;
36use heapless::consts::*;
37use heapless::String;
38
39fn convert_hours_to_text(hours: u8) -> &'static str {
40    match hours % 12 {
41        0 => "twaalf",
42        1 => "één",
43        2 => "twee",
44        3 => "drie",
45        4 => "vier",
46        5 => "vijf",
47        6 => "zes",
48        7 => "zeven",
49        8 => "acht",
50        9 => "negen",
51        10 => "tien",
52        11 => "elf",
53        _ => "",
54    }
55}
56
57fn convert_time_to_text(time: &Time) -> String<U20> {
58    let mut text = String::<U20>::new();
59
60    let rounded_time = time.round_to_quarters();
61    if rounded_time.minutes_local() == 0 {
62        write!(
63            &mut text,
64            "{}\nuur",
65            convert_hours_to_text(rounded_time.hours_local())
66        )
67        .unwrap()
68    } else if rounded_time.minutes_local() == 15 {
69        write!(
70            &mut text,
71            "kwart\nover\n{}",
72            convert_hours_to_text(rounded_time.hours_local())
73        )
74        .unwrap()
75    } else if rounded_time.minutes_local() == 30 {
76        write!(
77            &mut text,
78            "half\n{}",
79            convert_hours_to_text(rounded_time.hours_local() + 1)
80        )
81        .unwrap()
82    } else if rounded_time.minutes_local() == 45 {
83        write!(
84            &mut text,
85            "kwart\nvoor\n{}",
86            convert_hours_to_text(rounded_time.hours_local() + 1)
87        )
88        .unwrap()
89    }
90
91    text
92}
93
94#[derive(Default)]
95pub struct TextualTimeWatchfaceStyle<C> {
96    _phantom_data: PhantomData<C>,
97}
98
99impl<C> Drawable for Styled<Watchface, TextualTimeWatchfaceStyle<C>>
100where
101    C: RgbColor,
102{
103    type Color = C;
104
105    type Output = ();
106
107    fn draw<D: DrawTarget<Color = C>>(
108        &self,
109        display: &mut D,
110    ) -> Result<(), <D as DrawTarget>::Error> {
111        let display_area = display.bounding_box();
112
113        display.clear(C::BLACK)?;
114
115        if let Some(time) = &self.watchface.time {
116            let time_text_style = MonoTextStyleBuilder::new()
117                .font(&FONT_10X20)
118                .text_color(C::WHITE)
119                .background_color(C::BLACK)
120                .build();
121
122            let text = convert_time_to_text(time);
123
124            Text::new(&text, Point::new(10, 70), time_text_style)
125                .align_to(&display_area, horizontal::Center, vertical::Center)
126                .draw(display)?;
127        }
128
129        let battery_text = if let Some(battery) = &self.watchface.battery {
130            let mut battery_text = String::<U12>::new();
131            write!(&mut battery_text, "{:02}%", battery.percentage()).unwrap();
132            battery_text
133        } else {
134            String::<U12>::new()
135        };
136
137        let charger_text = match &self.watchface.charger {
138            Some(ChargerState::Charging) => "Charging",
139            Some(ChargerState::Full) => "Full",
140            Some(ChargerState::Discharging) => "",
141            None => "",
142        };
143
144        let text_style = MonoTextStyleBuilder::new()
145            .font(&FONT_9X15_BOLD)
146            .text_color(C::WHITE)
147            .build();
148
149        LinearLayout::vertical(
150            Chain::new(Text::new(
151                battery_text.as_str(),
152                Point::zero(),
153                text_style.clone(),
154            ))
155            .append(Text::new(charger_text, Point::zero(), text_style)),
156        )
157        .with_alignment(horizontal::Right)
158        .arrange()
159        .align_to(&display_area, horizontal::Right, vertical::Top)
160        .draw(display)?;
161
162        Ok(())
163    }
164}