Skip to main content

Surface

Struct Surface 

Source
pub struct Surface<M: SurfaceModel> { /* private fields */ }
Expand description

A surface in model M (Cells or Pixels). See the module docs.

Implementations§

Source§

impl Surface<Pixels>

Source

pub fn blit_clipped( &mut self, x: i32, y: i32, image: Image<'_>, clip: Area, blend: bool, )

Draws image with its top-left corner at (x, y), clipped to clip and to the surface: only pixels inside both are written and marked dirty, so a sprite drawn into a viewport never spills out of it. With blend each pixel is drawn over what is there with its alpha; without, it replaces it. A short rgba draws the rows it holds.

Source

pub fn fill_rect_at(&mut self, x: f32, y: f32, w: f32, h: f32, colour: Rgba)

A rectangle at a fractional position and size, its edges antialiased: a paddle or a ball that moves by less than a pixel per frame moves smoothly.

Source

pub fn label_scaled( &mut self, x: i32, y: i32, text: &str, colour: Rgba, scale: u32, ) -> u32

Text in the built-in 5x7 font at an integer scale (1 is 5x7 pixels per glyph). Returns the pixels advanced.

Source§

impl Surface<Cells>

Source

pub fn put(&mut self, x: u32, y: u32, character: char, style: Style) -> u32

Writes one character at (x, y). Returns the columns it took: 1, 2 for a wide character, 0 when nothing was written (out of bounds, or a character a cell never shows). A wide character that does not fit before the right edge leaves one empty cell instead (the viewer would drop it).

Source

pub fn text(&mut self, x: u32, y: u32, text: &str, style: Style) -> u32

Writes text from (x, y) along one row, grapheme by grapheme, clipped at the right edge. Returns the columns written. Each grapheme shows as its first character (see the module docs).

Examples found in repository?
examples/together_ui.rs (line 57)
53    fn paint(&mut self) {
54        let (cols, _) = self.card.size();
55        self.card.clear();
56        let line = format!("{} · {} builds", self.status.state, self.status.builds);
57        self.card.text(0, 0, &line, Style::fg(Colour::FG));
58        let hint = "rebuild";
59        let x = cols.saturating_sub(hint.len() as u32);
60        self.card
61            .text(x, 0, hint, Style::fg(Colour::ACCENT).underline());
62        let _ = self.card.commit();
63    }
More examples
Hide additional examples
examples/clock_card.rs (line 43)
32    fn frame(&mut self, frame: &Frame) {
33        let second = frame.now_ms() / 1000;
34        if self.shown != Some(second) {
35            self.shown = Some(second);
36            let (cols, _) = self.card.size();
37            let time = format!(
38                "{:02}:{:02}:{:02}",
39                second / 3600 % 24,
40                second / 60 % 60,
41                second % 60
42            );
43            self.card.text(0, 0, &time, Style::fg(Colour::FG).bold());
44            let fraction = (second % 60) as f32 / 60.0;
45            let track = Style::fg(Colour::RECEDE_FG);
46            self.card.bar(
47                Area::new(0, 1, cols, 1),
48                fraction,
49                Style::fg(Colour::ACCENT),
50                track,
51            );
52            // Publishes only the cells that changed.
53            let _ = self.card.commit();
54        }
55        // The next second boundary: an idle viewer wakes once a second.
56        frame.wake_at((second + 1) * 1000);
57    }
Source

pub fn fill(&mut self, rect: Rect, character: char, style: Style)

Fills rect with character in style (clipped).

Source

pub fn clear(&mut self)

Empties every cell (theme text on a transparent background). The whole surface is dirty; the previous frame is not copied forward.

Examples found in repository?
examples/together_ui.rs (line 55)
53    fn paint(&mut self) {
54        let (cols, _) = self.card.size();
55        self.card.clear();
56        let line = format!("{} · {} builds", self.status.state, self.status.builds);
57        self.card.text(0, 0, &line, Style::fg(Colour::FG));
58        let hint = "rebuild";
59        let x = cols.saturating_sub(hint.len() as u32);
60        self.card
61            .text(x, 0, hint, Style::fg(Colour::ACCENT).underline());
62        let _ = self.card.commit();
63    }
Source

pub fn clear_with(&mut self, style: Style)

Empties every cell with style (its background shows).

Source

pub fn cell(&self, x: u32, y: u32) -> Option<Cell>

The cell at (x, y) as it will be committed next.

Source

pub fn row_text(&self, y: u32) -> String

One row as text: each cell’s character, a space for an empty cell, nothing for the right half of a wide character. For tests.

Source§

impl Surface<Pixels>

Source

pub fn set_pixel(&mut self, x: u32, y: u32, colour: Rgba)

Sets one pixel (no blending). Outside the surface nothing happens.

Source

pub fn blend_pixel(&mut self, x: i32, y: i32, colour: Rgba)

Draws colour over one pixel with its alpha.

Source

pub fn pixel(&self, x: u32, y: u32) -> Option<Rgba>

The pixel at (x, y) as it will be committed next.

Source

pub fn clear(&mut self, colour: Rgba)

Fills the whole surface with colour. The previous frame is not copied forward.

Examples found in repository?
examples/shapes_stage.rs (line 39)
24    fn frame(&mut self, frame: &Frame) {
25        let (w, h) = self.stage.size();
26        if w == 0 || h == 0 {
27            // No pixel geometry yet: a resize will follow.
28            return;
29        }
30        let theme = view::theme();
31        let (fg, bg, accent) = (
32            Rgba::hex(theme.fg),
33            Rgba::hex(theme.bg),
34            Rgba::hex(theme.accent),
35        );
36        let t = (frame.now_ms() % 4000) as f32 / 4000.0;
37
38        // A full repaint: nothing is carried forward from the last frame.
39        self.stage.clear(bg);
40        let panel = Area::new(8, 8, w.saturating_sub(16), h.saturating_sub(16));
41        self.stage
42            .fill_rounded_rect(panel, 12, Rgba::hex(theme.recede_bg));
43        self.stage
44            .rounded_rect(panel, 12, Rgba::hex(theme.recede_fg));
45
46        let (cx, cy) = (w as f32 / 2.0, h as f32 / 2.0);
47        let radius = (w.min(h) / 4) as f32;
48        let angle = t * core::f32::consts::TAU;
49        // `no_std` has no `f32::sin`: the SDK re-exports `libm`. The dot
50        // moves between pixels; the shapes antialias it there.
51        let dot = (
52            cx + radius * libm::cosf(angle),
53            cy + radius * libm::sinf(angle),
54        );
55        self.stage
56            .circle((cx, cy), radius, Rgba::hex(theme.recede_fg));
57        self.stage
58            .line((cx as i32, cy as i32), (dot.0 as i32, dot.1 as i32), accent);
59        self.stage.fill_circle(dot, 6.0, accent);
60
61        let bar = Area::new(24, h as i32 - 40, w.saturating_sub(48), 8);
62        self.stage
63            .bar(bar, t, accent, Rgba::hex(theme.recede_fg).with_alpha(96));
64        self.stage.label(24, 24, "shapes", fg);
65        let _ = self.stage.commit();
66
67        match frame.power() {
68            Power::Mains => frame.request_frame(),
69            Power::SavePower => frame.wake_after(1000),
70        }
71    }
Source

pub fn blit(&mut self, x: i32, y: i32, width: u32, height: u32, rgba: &[u8])

Copies an RGBA8 image of width by height pixels (width * 4 bytes per row) to (x, y), clipped to the surface; no blending. A short rgba copies the rows it holds. Surface::blit_clipped clips to a rectangle too and can blend.

Source

pub fn pixels_mut(&mut self) -> RefMut<'_, [u8]>

The whole frame being painted, as RGBA8 bytes, holding what was last committed. The whole surface is marked dirty; for a small change prefer the drawing methods or Surface::mark_dirty after Surface::pixels_untracked. Empty while the surface has no pixels.

Source

pub fn pixels_untracked(&mut self) -> RefMut<'_, [u8]>

As Surface::pixels_mut, recording nothing: mark what you change with Surface::mark_dirty.

Source

pub fn repaint_all(&mut self) -> RefMut<'_, [u8]>

The whole frame for a full repaint: its contents are stale (what was committed two frames ago), nothing is copied forward, and the whole surface is dirty. The cheapest way to draw an animation that repaints every pixel.

Source§

impl<M: SurfaceModel> Surface<M>

Source

pub fn new(id: &str) -> Self

The manifest’s surface id. Binds at once when the viewer has laid it out, else on its first crate::Event::Resize.

Examples found in repository?
examples/shapes_stage.rs (line 20)
18    fn activate(_cx: &mut Context) -> Self {
19        Self {
20            stage: Surface::new("stage"),
21        }
22    }
More examples
Hide additional examples
examples/clock_card.rs (line 27)
25    fn activate(_cx: &mut Context) -> Self {
26        Self {
27            card: Surface::new("card"),
28            shown: None,
29        }
30    }
examples/together_ui.rs (line 70)
67    fn activate(cx: &mut Context) -> Self {
68        let _ = cx.events().on("together.*");
69        let mut plugin = Self {
70            card: Surface::new("card"),
71            status: Status::default(),
72        };
73        plugin.reload(cx);
74        plugin
75    }
Source

pub fn offscreen(geometry: Geometry) -> Self

A surface of the plugin’s own that the host never sees: for tests, and to draw into a buffer before blitting it. Commits are recorded (Surface::last_commit) but go nowhere.

Source

pub fn resize_offscreen(&mut self, geometry: Geometry)

Resizes an offscreen surface as a viewer resize would: a new zeroed region, the next commit fully dirty. No-op for a host surface (the host resizes those).

Source

pub fn id(&self) -> String

Source

pub fn geometry(&self) -> Option<Geometry>

The current geometry; None until the viewer lays the surface out.

Source

pub fn size(&self) -> (u32, u32)

Width and height in the model’s units: cells, or pixels.

Examples found in repository?
examples/together_ui.rs (line 54)
53    fn paint(&mut self) {
54        let (cols, _) = self.card.size();
55        self.card.clear();
56        let line = format!("{} · {} builds", self.status.state, self.status.builds);
57        self.card.text(0, 0, &line, Style::fg(Colour::FG));
58        let hint = "rebuild";
59        let x = cols.saturating_sub(hint.len() as u32);
60        self.card
61            .text(x, 0, hint, Style::fg(Colour::ACCENT).underline());
62        let _ = self.card.commit();
63    }
More examples
Hide additional examples
examples/clock_card.rs (line 36)
32    fn frame(&mut self, frame: &Frame) {
33        let second = frame.now_ms() / 1000;
34        if self.shown != Some(second) {
35            self.shown = Some(second);
36            let (cols, _) = self.card.size();
37            let time = format!(
38                "{:02}:{:02}:{:02}",
39                second / 3600 % 24,
40                second / 60 % 60,
41                second % 60
42            );
43            self.card.text(0, 0, &time, Style::fg(Colour::FG).bold());
44            let fraction = (second % 60) as f32 / 60.0;
45            let track = Style::fg(Colour::RECEDE_FG);
46            self.card.bar(
47                Area::new(0, 1, cols, 1),
48                fraction,
49                Style::fg(Colour::ACCENT),
50                track,
51            );
52            // Publishes only the cells that changed.
53            let _ = self.card.commit();
54        }
55        // The next second boundary: an idle viewer wakes once a second.
56        frame.wake_at((second + 1) * 1000);
57    }
examples/shapes_stage.rs (line 25)
24    fn frame(&mut self, frame: &Frame) {
25        let (w, h) = self.stage.size();
26        if w == 0 || h == 0 {
27            // No pixel geometry yet: a resize will follow.
28            return;
29        }
30        let theme = view::theme();
31        let (fg, bg, accent) = (
32            Rgba::hex(theme.fg),
33            Rgba::hex(theme.bg),
34            Rgba::hex(theme.accent),
35        );
36        let t = (frame.now_ms() % 4000) as f32 / 4000.0;
37
38        // A full repaint: nothing is carried forward from the last frame.
39        self.stage.clear(bg);
40        let panel = Area::new(8, 8, w.saturating_sub(16), h.saturating_sub(16));
41        self.stage
42            .fill_rounded_rect(panel, 12, Rgba::hex(theme.recede_bg));
43        self.stage
44            .rounded_rect(panel, 12, Rgba::hex(theme.recede_fg));
45
46        let (cx, cy) = (w as f32 / 2.0, h as f32 / 2.0);
47        let radius = (w.min(h) / 4) as f32;
48        let angle = t * core::f32::consts::TAU;
49        // `no_std` has no `f32::sin`: the SDK re-exports `libm`. The dot
50        // moves between pixels; the shapes antialias it there.
51        let dot = (
52            cx + radius * libm::cosf(angle),
53            cy + radius * libm::sinf(angle),
54        );
55        self.stage
56            .circle((cx, cy), radius, Rgba::hex(theme.recede_fg));
57        self.stage
58            .line((cx as i32, cy as i32), (dot.0 as i32, dot.1 as i32), accent);
59        self.stage.fill_circle(dot, 6.0, accent);
60
61        let bar = Area::new(24, h as i32 - 40, w.saturating_sub(48), 8);
62        self.stage
63            .bar(bar, t, accent, Rgba::hex(theme.recede_fg).with_alpha(96));
64        self.stage.label(24, 24, "shapes", fg);
65        let _ = self.stage.commit();
66
67        match frame.power() {
68            Power::Mains => frame.request_frame(),
69            Power::SavePower => frame.wake_after(1000),
70        }
71    }
Source

pub fn request_size(&self, cols: u32, rows: u32) -> Result<()>

Asks the viewer for cols by rows cells (0: its choice in that dimension) instead of the manifest’s size; see Context::request_size.

Source

pub fn set_label(&self, label: Option<&str>) -> Result<()>

Sets the short label the viewer shows at the right end of this surface’s chrome title; see Context::set_label.

Source

pub fn set_caret(&self, caret: Option<(u32, u32)>) -> Result<()>

Places the viewer’s text cursor at (col, row) of this surface while it takes keys, or removes it; see Context::set_caret.

Source

pub fn is_attached(&self) -> bool

Source

pub fn is_dirty(&self) -> bool

Whether anything was drawn since the last commit (or the next commit is the first at a new size).

Source

pub fn mark_dirty(&mut self, rect: Rect)

Records a rectangle as changed, after writing through a raw slice.

Source

pub fn commit(&mut self) -> Result<bool>

Publishes what was drawn since the last commit and switches slots. Ok(false) when nothing changed (no host call, no frame). The first commit after a (re)bind covers the whole surface.

Examples found in repository?
examples/together_ui.rs (line 62)
53    fn paint(&mut self) {
54        let (cols, _) = self.card.size();
55        self.card.clear();
56        let line = format!("{} · {} builds", self.status.state, self.status.builds);
57        self.card.text(0, 0, &line, Style::fg(Colour::FG));
58        let hint = "rebuild";
59        let x = cols.saturating_sub(hint.len() as u32);
60        self.card
61            .text(x, 0, hint, Style::fg(Colour::ACCENT).underline());
62        let _ = self.card.commit();
63    }
More examples
Hide additional examples
examples/clock_card.rs (line 53)
32    fn frame(&mut self, frame: &Frame) {
33        let second = frame.now_ms() / 1000;
34        if self.shown != Some(second) {
35            self.shown = Some(second);
36            let (cols, _) = self.card.size();
37            let time = format!(
38                "{:02}:{:02}:{:02}",
39                second / 3600 % 24,
40                second / 60 % 60,
41                second % 60
42            );
43            self.card.text(0, 0, &time, Style::fg(Colour::FG).bold());
44            let fraction = (second % 60) as f32 / 60.0;
45            let track = Style::fg(Colour::RECEDE_FG);
46            self.card.bar(
47                Area::new(0, 1, cols, 1),
48                fraction,
49                Style::fg(Colour::ACCENT),
50                track,
51            );
52            // Publishes only the cells that changed.
53            let _ = self.card.commit();
54        }
55        // The next second boundary: an idle viewer wakes once a second.
56        frame.wake_at((second + 1) * 1000);
57    }
examples/shapes_stage.rs (line 65)
24    fn frame(&mut self, frame: &Frame) {
25        let (w, h) = self.stage.size();
26        if w == 0 || h == 0 {
27            // No pixel geometry yet: a resize will follow.
28            return;
29        }
30        let theme = view::theme();
31        let (fg, bg, accent) = (
32            Rgba::hex(theme.fg),
33            Rgba::hex(theme.bg),
34            Rgba::hex(theme.accent),
35        );
36        let t = (frame.now_ms() % 4000) as f32 / 4000.0;
37
38        // A full repaint: nothing is carried forward from the last frame.
39        self.stage.clear(bg);
40        let panel = Area::new(8, 8, w.saturating_sub(16), h.saturating_sub(16));
41        self.stage
42            .fill_rounded_rect(panel, 12, Rgba::hex(theme.recede_bg));
43        self.stage
44            .rounded_rect(panel, 12, Rgba::hex(theme.recede_fg));
45
46        let (cx, cy) = (w as f32 / 2.0, h as f32 / 2.0);
47        let radius = (w.min(h) / 4) as f32;
48        let angle = t * core::f32::consts::TAU;
49        // `no_std` has no `f32::sin`: the SDK re-exports `libm`. The dot
50        // moves between pixels; the shapes antialias it there.
51        let dot = (
52            cx + radius * libm::cosf(angle),
53            cy + radius * libm::sinf(angle),
54        );
55        self.stage
56            .circle((cx, cy), radius, Rgba::hex(theme.recede_fg));
57        self.stage
58            .line((cx as i32, cy as i32), (dot.0 as i32, dot.1 as i32), accent);
59        self.stage.fill_circle(dot, 6.0, accent);
60
61        let bar = Area::new(24, h as i32 - 40, w.saturating_sub(48), 8);
62        self.stage
63            .bar(bar, t, accent, Rgba::hex(theme.recede_fg).with_alpha(96));
64        self.stage.label(24, 24, "shapes", fg);
65        let _ = self.stage.commit();
66
67        match frame.power() {
68            Power::Mains => frame.request_frame(),
69            Power::SavePower => frame.wake_after(1000),
70        }
71    }
Source

pub fn last_commit(&self) -> Option<CommitInfo>

What the last commit published.

Source

pub fn pending_dirty(&self) -> Vec<Rect>

The rectangles the next commit would send (without the fully dirty first commit’s).

Source

pub fn detach(&mut self) -> Result<()>

Unbinds the surface from the host. Dropping the surface does this.

Trait Implementations§

Source§

impl<M: Debug + SurfaceModel> Debug for Surface<M>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<M: SurfaceModel> Drop for Surface<M>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl Shapes for Surface<Cells>

Source§

fn fill_rect(&mut self, area: Area, ink: Style)

Full blocks in the style’s foreground. For a background panel use Surface::fill with a space and a background colour.

Source§

fn bar(&mut self, area: Area, fraction: f32, fill: Style, track: Style)

Full blocks in fill, one eighth block for the partial cell, and ░ in track for the rest.

Source§

fn fill_rounded_rect(&mut self, area: Area, radius: u32, ink: Style)

Full blocks, with quarter blocks in the corners when radius > 0.

Source§

type Ink = Style

Source§

fn stroke_rect(&mut self, area: Area, ink: Style)

A one-unit outline of area.
Source§

fn line(&mut self, from: (i32, i32), to: (i32, i32), ink: Style)

A line from from to to, both ends included.
Source§

fn rounded_rect(&mut self, area: Area, radius: u32, ink: Style)

The outline of area with corners of radius (cells round their corners with arcs whatever the radius; a zero radius is square).
Source§

fn circle(&mut self, centre: (f32, f32), radius: f32, ink: Style)

A circle’s outline around centre, in continuous coordinates: the unit at (x, y) spans x..x + 1, so (x + 0.5, y + 0.5) is its centre, and a moving shape can sit between units (pixels antialias it; cells round to the nearest cell). Cells are about twice as tall as wide, so in cells the radius counts rows and spans twice as many columns.
Source§

fn fill_circle(&mut self, centre: (f32, f32), radius: f32, ink: Style)

A filled circle (see Shapes::circle).
Source§

fn label(&mut self, x: i32, y: i32, text: &str, ink: Style) -> u32

Text from (x, y), clipped: cells write graphemes; pixels draw the built-in 5x7 font (GLYPH_ADVANCE pixels per character, ASCII; other characters show as a box). Returns the units advanced.
Source§

impl Shapes for Surface<Pixels>

Source§

type Ink = Rgba

Source§

fn stroke_rect(&mut self, area: Area, ink: Rgba)

A one-unit outline of area.
Source§

fn fill_rect(&mut self, area: Area, ink: Rgba)

area, filled.
Source§

fn line(&mut self, from: (i32, i32), to: (i32, i32), ink: Rgba)

A line from from to to, both ends included.
Source§

fn bar(&mut self, area: Area, fraction: f32, fill: Rgba, track: Rgba)

A horizontal bar across area: the left fraction (0 to 1) in fill, the rest in track. Partial units are shown with eighth blocks (cells) or coverage (pixels).
Source§

fn rounded_rect(&mut self, area: Area, radius: u32, ink: Rgba)

The outline of area with corners of radius (cells round their corners with arcs whatever the radius; a zero radius is square).
Source§

fn fill_rounded_rect(&mut self, area: Area, radius: u32, ink: Rgba)

area filled, with corners of radius.
Source§

fn circle(&mut self, centre: (f32, f32), radius: f32, ink: Rgba)

A circle’s outline around centre, in continuous coordinates: the unit at (x, y) spans x..x + 1, so (x + 0.5, y + 0.5) is its centre, and a moving shape can sit between units (pixels antialias it; cells round to the nearest cell). Cells are about twice as tall as wide, so in cells the radius counts rows and spans twice as many columns.
Source§

fn fill_circle(&mut self, centre: (f32, f32), radius: f32, ink: Rgba)

A filled circle (see Shapes::circle).
Source§

fn label(&mut self, x: i32, y: i32, text: &str, ink: Rgba) -> u32

Text from (x, y), clipped: cells write graphemes; pixels draw the built-in 5x7 font (GLYPH_ADVANCE pixels per character, ASCII; other characters show as a box). Returns the units advanced.

Auto Trait Implementations§

§

impl<M> !RefUnwindSafe for Surface<M>

§

impl<M> !Send for Surface<M>

§

impl<M> !Sync for Surface<M>

§

impl<M> !UnwindSafe for Surface<M>

§

impl<M> Freeze for Surface<M>
where PhantomData<M>: Freeze,

§

impl<M> Unpin for Surface<M>
where PhantomData<M>: Unpin,

§

impl<M> UnsafeUnpin for Surface<M>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Resource for T
where T: 'static,

Source§

type Rep = Option<T>

The type which is actually stored in-memory for this resource. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.