multiple_displays/
multiple-displays.rs

1//! # Example: Multiple displays
2//!
3//! This example demonstrates how multiple displays can be displayed in a common window.
4
5extern crate embedded_graphics;
6extern crate embedded_graphics_simulator;
7
8use embedded_graphics::{
9    geometry::AnchorPoint,
10    mono_font::{ascii::FONT_10X20, MonoTextStyle},
11    pixelcolor::{BinaryColor, Rgb565, Rgb888},
12    prelude::*,
13    primitives::{Circle, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, StrokeAlignment},
14    text::{Alignment, Baseline, Text, TextStyle, TextStyleBuilder},
15};
16use embedded_graphics_simulator::{
17    sdl2::MouseButton, BinaryColorTheme, MultiWindow, OutputSettings, OutputSettingsBuilder,
18    SimulatorDisplay, SimulatorEvent,
19};
20
21const OLED_TEXT: MonoTextStyle<BinaryColor> = MonoTextStyle::new(&FONT_10X20, BinaryColor::On);
22const TFT_TEXT: MonoTextStyle<Rgb565> =
23    MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_LIGHT_SLATE_GRAY);
24const CENTERED: TextStyle = TextStyleBuilder::new()
25    .alignment(Alignment::Center)
26    .baseline(Baseline::Middle)
27    .build();
28
29/// Determines the position of a display.
30fn display_offset(window_size: Size, display_size: Size, anchor_point: AnchorPoint) -> Point {
31    // Position displays in a rectangle that is 20px than the the window.
32    let layout_rect = Rectangle::new(Point::zero(), window_size).offset(-20);
33
34    // Resize the rectangle to the display size to determine the offset from the
35    // top left corner of the window to the top left corner of the display.
36    layout_rect.resized(display_size, anchor_point).top_left
37}
38
39fn main() -> Result<(), core::convert::Infallible> {
40    // Create three simulated monochrome 128x64 OLED displays.
41
42    let mut oled_displays = Vec::new();
43    for i in 0..3 {
44        let mut oled: SimulatorDisplay<BinaryColor> = SimulatorDisplay::new(Size::new(128, 64));
45
46        Text::with_text_style(
47            &format!("Display {i}"),
48            oled.bounding_box().center(),
49            OLED_TEXT,
50            CENTERED,
51        )
52        .draw(&mut oled)
53        .unwrap();
54
55        oled_displays.push(oled);
56    }
57
58    // Create a simulated color 320x240 TFT display.
59
60    let mut tft: SimulatorDisplay<Rgb565> = SimulatorDisplay::new(Size::new(320, 240));
61    tft.clear(Rgb565::new(5, 10, 5)).unwrap();
62
63    Text::with_text_style(
64        &format!("Draw here"),
65        tft.bounding_box().center(),
66        TFT_TEXT,
67        CENTERED,
68    )
69    .draw(&mut tft)
70    .unwrap();
71
72    // The simulated displays can now be added to common simulator window.
73
74    let window_size = Size::new(1300, 500);
75    let mut window = MultiWindow::new("Multiple displays example", window_size);
76    window.clear(Rgb888::CSS_DIM_GRAY);
77
78    let oled_settings = OutputSettingsBuilder::new()
79        .theme(BinaryColorTheme::OledBlue)
80        .scale(2)
81        .build();
82    let oled_size = oled_displays[0].output_size(&oled_settings);
83
84    for (oled, anchor) in oled_displays.iter().zip(
85        [
86            AnchorPoint::TopLeft,
87            AnchorPoint::TopCenter,
88            AnchorPoint::TopRight,
89        ]
90        .into_iter(),
91    ) {
92        let offset = display_offset(window_size, oled_size, anchor);
93        window.add_display(&oled, offset, &oled_settings);
94    }
95
96    let tft_settings = OutputSettings::default();
97    let tft_size = tft.output_size(&tft_settings);
98    let tft_offset = display_offset(window_size, tft_size, AnchorPoint::BottomCenter);
99
100    window.add_display(&tft, tft_offset, &tft_settings);
101
102    let border_style = PrimitiveStyleBuilder::new()
103        .stroke_width(5)
104        .stroke_alignment(StrokeAlignment::Inside)
105        .build();
106
107    let mut mouse_down = false;
108
109    'running: loop {
110        // Call `update_display` for all display. Note that the window won't be
111        // updated until `window.flush` is called.
112        for oled in &oled_displays {
113            window.update_display(oled);
114        }
115        window.update_display(&tft);
116        window.flush();
117
118        for event in window.events() {
119            match event {
120                SimulatorEvent::MouseMove { point } => {
121                    // Mouse events use the window coordinate system.
122                    // `translate_mouse_position` can be used to translate the
123                    // mouse position into the display coordinate system.
124
125                    for oled in &mut oled_displays {
126                        let is_inside = window.translate_mouse_position(oled, point).is_some();
127
128                        let style = PrimitiveStyleBuilder::from(&border_style)
129                            .stroke_color(BinaryColor::from(is_inside))
130                            .build();
131
132                        oled.bounding_box().into_styled(style).draw(oled).unwrap();
133                    }
134
135                    if mouse_down {
136                        if let Some(point) = window.translate_mouse_position(&tft, point) {
137                            Circle::with_center(point, 10)
138                                .into_styled(PrimitiveStyle::with_fill(Rgb565::CSS_DODGER_BLUE))
139                                .draw(&mut tft)
140                                .unwrap();
141                        }
142                    }
143                }
144                SimulatorEvent::MouseButtonDown {
145                    mouse_btn: MouseButton::Left,
146                    ..
147                } => {
148                    mouse_down = true;
149                }
150                SimulatorEvent::MouseButtonUp {
151                    mouse_btn: MouseButton::Left,
152                    ..
153                } => {
154                    mouse_down = false;
155                }
156                SimulatorEvent::Quit => break 'running,
157                _ => {}
158            }
159        }
160    }
161
162    Ok(())
163}