Skip to main content

Surface

Struct Surface 

Source
pub struct Surface<'a> { /* private fields */ }
Expand description

The render target for every drawing call in the workspace: a mutable reference to a Grid plus a fixed layer, scoped to an area and clipped to a clip rect.

A Surface is typically created once per frame, scoped to the whole drawing surface (e.g. via Terminal::draw), and handed to every subsystem/widget in turn; each caller’s own area: Rect (a sub-rect of the surface’s own area, e.g. one produced by a layout split) is relative to this surface’s own area origin, not to the underlying grid. Surface::put/Surface::print/… take coordinates in that same local space, where (0, 0) is area’s top-left corner, and silently drop any write that falls outside Surface::clip_rect, matching the rest of the workspace’s clip-on-draw policy for out-of-bounds drawing.

area and clip_rect answer two different questions. area is the region this surface represents: what a widget lays itself out in, and what width/ height report. clip_rect is the subset of area that is actually visible: what every write is bounds-checked against. The two start out equal (see Surface::new) and diverge once Surface::clip or Surface::scope is used.

Surface::clip narrows what is visible without changing what this surface represents: clip_rect is intersected with the given rect, area is untouched. Surface::scope does both: area becomes the given rect and clip_rect is intersected with it, which is what a widget’s own sub-surface needs when it should be laid out against a new rect but still bounded by whatever was already visible. Both narrow monotonically: neither can widen clip_rect beyond what the parent surface already allowed.

A caller that genuinely needs more than one layer at once (e.g. a modal dimming layer 0 while drawing its own content on layer 1) switches layers with Surface::on_layer/Surface::on_tier rather than being restricted to the layer it was constructed with.

Implementations§

Source§

impl Surface<'_>

Source

pub fn put(&mut self, pos: impl Into<Pos>, ch: char, style: Style)

Place ch at pos in style. A no-op if pos is outside this surface’s clip.

If a pixel backend resolves ch to a sprite, that sprite is composited from its own pixels: style.fg does not tint it, and style.bg shows through only where the sprite is transparent. See put_span.

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Pos, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(4, 4);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0);

surface.put((1, 1), 'X', Style::default());
// Outside the surface's clip: silently dropped, not a panic.
surface.put((10, 10), 'X', Style::default());

assert_eq!(grid[Pos::new(1, 1)].glyph(), 'X');
Source

pub fn put_signed(&mut self, pos: (i32, i32), ch: char, style: Style)

put, in coordinates relative to this surface’s own area origin, where a negative coordinate is expressible and simply falls outside (a no-op, matching put’s out-of-bounds behavior). A coordinate that stays non-negative but exceeds u16::MAX after this surface’s translate offset is subtracted is dropped the same way: it addresses a cell this surface’s u16 grid space cannot name.

Scrolling/camera code (e.g. a viewport over a wider world) computes positions in a coordinate space that can go negative relative to the viewport, which Pos (backed by u16) cannot even express. put_signed takes that arithmetic directly, so a caller no longer clip-tests by hand before calling put.

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Pos, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(4, 4);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0);

// Negative in either axis: outside this surface's area, silently dropped.
surface.put_signed((-1, 1), 'X', Style::default());
// Non-negative and within bounds: lands like `put`.
surface.put_signed((1, 1), 'X', Style::default());

assert_eq!(grid[Pos::new(1, 1)].glyph(), 'X');
assert_eq!(grid[Pos::new(0, 1)].glyph(), ' ');
Source

pub fn fill_rect(&mut self, rect: Rect, ch: char, style: Style)

Fill rect (clipped to this surface’s own clip) with ch in style.

rect is local to this surface’s own area: (0, 0) is area’s own top-left, not the grid’s, the same convention clear_region and print_aligned use for their own rect (not absolute grid coordinates, the convention clip/scope use).

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Pos, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(4, 4);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0);

// `rect` extends well past the grid on both axes; only the cells inside the
// surface's own clip are touched, the rest is silently clipped.
surface.fill_rect(Rect::new(2, 2, 10, 10), '#', Style::default());

assert_eq!(grid[Pos::new(3, 3)].glyph(), '#');
assert_eq!(grid[Pos::new(0, 0)].glyph(), ' ');
Source

pub fn blit(&mut self, grid: &Grid, x: u16, y: u16)

Stamps grid’s layer 0 onto this surface’s own layer, with its top-left cell at (x, y) (local to this surface’s area, matching put’s convention), clipped to this surface’s clip.

Always reads grid’s layer 0, regardless of which layer this surface itself is currently writing to: grid is typically a standalone buffer composed elsewhere (e.g. BoxStyle::render‘s output, or retroglyph-uijoin_h/join_v), and per their own docs those only ever populate layer 0. Reading this surface’s own layer off grid instead (what Grid::blit’s single layer parameter would do if called directly) finds nothing there whenever this surface isn’t on layer 0, and the copy silently does nothing.

Unlike a single-cell put, a write that starts outside this surface’s clip is not necessarily dropped whole: the part of grid that does land inside the clip is copied, matching fill_rect’s per-cell clipping rather than put_span’s all-or-nothing footprint check, since grid is arbitrary composed content rather than one indivisible sprite.

Unlike put and the rest of this surface’s single-sprite writes, this does not apply with_tint’s tint: a tint lands on one sprite’s anchor cell, and grid is arbitrary composed content with no single anchor to land it on, the same reason Grid::blit_cross_layer (this method’s own cross-layer copy, internal to Grid) carries no tint either. A tinted surface’s blit copies grid through unchanged.

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Rect};
use retroglyph_core::surface::{Layer, Surface};
use retroglyph_core::tile::Tile;

let mut src = Grid::new(2, 2);
src.put_tile(0, (0, 0), Tile::new('x', Style::default()));

let mut dst = Grid::new(4, 4);
let mut surface = Surface::new(&mut dst, Rect::new(0, 0, 4, 4), Layer::World.as_u8());

// `surface` is on the overlay tier; `src` only ever has layer 0, but `blit` reads that
// layer regardless, so the copy still lands (unlike `Grid::blit(surface.layer(), ...)`).
surface.on_tier(Layer::Overlay).blit(&src, 1, 1);

assert_eq!(dst.tile(Layer::Overlay.as_u8(), (1, 1)).map(Tile::glyph), Some('x'));
Source

pub fn clear(&mut self)

Clears this surface’s own area, intersected with its clip (on its own layer), back to Tile::default.

Source

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

Clears rect (clipped to this surface’s own clip, on its own layer) back to Tile::default.

rect is local to this surface’s own area, the same convention fill_rect and print_aligned use for their own rect (not absolute grid coordinates, the convention clip/scope use).

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Pos, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(4, 4);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0);
surface.fill_rect(Rect::new(0, 0, 4, 4), '#', Style::default());

// `rect` extends past the surface's own clip; only the overlap is cleared.
surface.clear_region(Rect::new(2, 2, 10, 10));

assert_eq!(grid[Pos::new(2, 2)].glyph(), ' ');
assert_eq!(grid[Pos::new(1, 1)].glyph(), '#');
Source§

impl Surface<'_>

Source

pub fn put_span<S: AsRef<str>>( &mut self, pos: impl Into<Pos>, rows: &[S], style: Style, ) -> Option<()>

Writes a multi-cell span at pos on this surface’s layer in style: one piece of artwork occupying a block of cells rather than one, the Surface twin of Grid::write_span.

rows holds one string per row of the footprint. Its first character is the anchor glyph, which a pixel backend looks up in its sprite cache; the rest are the span’s text fallback, printed by cell backends and skipped by pixel backends. Any AsRef<str> row works, so a literal footprint (&["[==]", "|__|"]) and a computed one (&Vec<String>) both pass without a borrowing pass over the rows; for the uniform case, see put_span_uniform.

See Grid::write_span for the full write semantics, and Grid::span_owner to hit-test the whole footprint.

§style applies to the text fallback, not to the sprite

A sprite is composited from its own pixels. style.fg does not tint it; style.bg is still painted behind it, so it shows through wherever the sprite is transparent. Recoloring a shared sprite per cell is therefore not possible: draw a variant of the artwork instead, which is the usual tileset idiom.

style is not dead on such a cell, because the same span drawn by a cell backend renders the text fallback in it. The consequence is that fg reads very differently depending on the backend, and that a glyph missing from the sprite cache silently falls back to a font glyph that is fg-colored, which looks a lot like a tint working.

§Returns

Some(()) once the whole span is written, or None having written nothing at all when rows is empty or ragged, either axis exceeds 255 cells, or the footprint does not fit entirely within this surface’s own clip (not just the grid) at pos. The surface has strictly more ways to refuse a span than Grid::write_span does, so a sprite that did not draw is answered here rather than in the backend.

Source

pub fn put_span_uniform( &mut self, pos: impl Into<Pos>, size: impl Into<Size>, anchor: char, fill: char, style: Style, ) -> Option<()>

Writes a size multi-cell span at pos on this surface’s layer in style: anchor in the anchor cell, fill in every other cell of the footprint, the Surface twin of Grid::write_span_uniform.

The uniform case of put_span, and what a sheet-driven renderer usually wants: one sprite, chosen at runtime, with the cells it covers blanked so nothing shows through its transparent pixels. fill is the text fallback a cell backend prints for those covered cells, so ' ' blanks them and a visible character keeps the footprint legible in a terminal.

style reads exactly as it does for put_span: it applies to the text fallback, never to the sprite.

§Returns

Some(()) once the whole span is written, or None having written nothing at all when either axis of size is 0 or exceeds 255 cells, or the footprint does not fit entirely within this surface’s own clip at pos.

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(8, 4);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 8, 4), 0);

// A 16x16 sprite over a 2x1 block of 8x16 cells, anchored at a runtime glyph.
let anchor = '\u{E000}';
surface.put_span_uniform((1, 1), (2, 1), anchor, ' ', Style::default())?;
Source

pub fn put_offset( &mut self, pos: impl Into<Pos>, offset: impl Into<Offset>, ch: char, style: Style, )

Place ch at pos with a sub-cell pixel offset, in style.

Sub-cell offsets are visual only: they do not affect grid logic or hit-testing. Backends that cannot represent pixel offsets (e.g. CrosstermBackend) ignore them. A no-op if pos is outside this surface’s clip.

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Offset, Pos, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(4, 4);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0);

// A large offset still lands the glyph in cell (1, 1): the offset is a pixel nudge
// for a pixel backend, never a coordinate shift.
surface.put_offset((1, 1), Offset::new(12, -12), 'X', Style::default());
// Outside the surface's clip: silently dropped, matching `put`.
surface.put_offset((10, 10), Offset::default(), 'X', Style::default());

assert_eq!(grid[Pos::new(1, 1)].glyph(), 'X');
Source§

impl Surface<'_>

Source

pub fn print(&mut self, pos: impl Into<Pos>, text: &str, style: Style)

Print text starting at pos in style.

\n advances to the next row at the original column. Text that would extend beyond this surface’s clip wraps to the next row at the original column; cells outside the clip (either axis) are dropped. When the egc feature is enabled, text is split into extended grapheme clusters (so combining marks and ZWJ sequences write as one cell each); otherwise it is split by char.

§Examples
use retroglyph_core::backend::Headless;
use retroglyph_core::color::Style;
use retroglyph_core::terminal::Terminal;

let mut term = Terminal::new(Headless::new(6, 3));
term.draw(|s| s.print((0, 0), "hello wrapped world", Style::default()))
    .unwrap();

// Wraps back to column 0 every 6 cells; the surface is only 3 rows tall, so
// the remainder past row 2 is clipped rather than growing the grid.
assert_eq!(
    term.backend().format_view(),
    "hello·\nwrappe\nd·worl\n",
);
Source

pub fn print_line(&mut self, pos: impl Into<Pos>, line: &Line)

Print line’s styled spans starting at pos, one row, each span in its own style. Stops once a span would start past this surface’s clip.

§Examples
use retroglyph_core::backend::Headless;
use retroglyph_core::text::{Line, Span};
use retroglyph_core::terminal::Terminal;

let mut term = Terminal::new(Headless::new(5, 2));
let line = Line::from(vec![Span::raw("hello"), Span::raw("world")]);
term.draw(|s| s.print_line((0, 0), &line)).unwrap();

// The first span exactly fills the one-row area. The second span would start at
// column 5, past the area, so it is skipped entirely rather than wrapped onto the
// next row the way `print` would wrap.
assert_eq!(term.backend().format_view(), "hello\n·····\n");
Source

pub fn print_aligned( &mut self, rect: Rect, text: &str, align: HAlign, style: Style, )

print, horizontally aligned within rect (clipped to this surface’s own clip) and measured in display columns (via unicode_width), not bytes.

rect is local to this surface’s own area, the same convention as fill_rect and clear_region (not absolute grid coordinates, the convention clip/scope use for their own rect): (0, 0) is area’s own top-left, so a widget’s own area().at_origin() can be passed straight in.

Wants a per-frame redrawn UI label (a status line, a centred title bar) that should not allocate: unlike TextLayout, which only accepts a Line (forcing an allocation to build one for every call), this takes &str directly.

The starting column is computed with saturating arithmetic, so text wider than rect does not panic or underflow: it simply left-aligns and lets print clip the overflow, for every HAlign (matching how HAlign::Center itself saturates in TextLayout).

Not gated behind the egc feature: unlike TextLayout, this needs nothing from it, so it’s reachable from any crate that only measures with unicode-width, including retroglyph-ui without opting into egc.

§Examples
use retroglyph_core::backend::Headless;
use retroglyph_core::layout::HAlign;
use retroglyph_core::color::Style;
use retroglyph_core::grid::Rect;
use retroglyph_core::terminal::Terminal;

let mut term = Terminal::new(Headless::new(6, 1));
term.draw(|s| {
    s.print_aligned(Rect::new(0, 0, 6, 1), "hi", HAlign::Center, Style::default())
})
.unwrap();

// "hi" is 2 columns wide in a 6-column rect: (6 - 2) / 2 == 2 columns of left padding.
assert_eq!(term.backend().format_view(), "··hi··\n");
Source§

impl<'a> Surface<'a>

Source

pub const fn area(&self) -> Rect

The region this surface represents, e.g. for a widget to lay itself out in.

Unlike clip_rect, this is never narrowed by clip: it only changes when scope sets a new one. A widget that reads its own area off the surface after being clipped (e.g. while partially offscreen) sees the region it was given, not the visible sliver of it, so it can still center itself correctly and let the clip take care of what actually lands.

Every drawing method on this surface (put, print, fill_rect, …) takes coordinates local to this surface, where (0, 0) is this area’s own top-left corner, not the underlying grid’s. area() itself is absolute grid space, so surface.put((surface.area().left(), ...), ...) only lands correctly for a surface whose area happens to start at the grid origin; anywhere else it silently misses. A widget that wants to place itself relative to its own bounds (e.g. a label in a corner) should reach for area().at_origin(), or just width/ height directly, and never for area()’s own left/ top.

§Examples
use retroglyph_core::grid::{Grid, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(10, 10);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0);
let mut scoped = surface.scope(Rect::new(3, 3, 4, 4));

assert_eq!(scoped.area(), Rect::new(3, 3, 4, 4));
assert_eq!(scoped.area().at_origin(), Rect::new(0, 0, 4, 4));
Source

pub const fn clip_rect(&self) -> Rect

The visible subset of area. Every write this surface accepts is bounds-checked against this rect, not area.

Source

pub const fn width(&self) -> u16

The width of this surface’s area, in columns.

Source

pub const fn height(&self) -> u16

The height of this surface’s area, in rows.

Source

pub const fn layer(&self) -> u8

The grid layer this surface writes to.

Source

pub const fn on_layer(&mut self, layer: u8) -> Surface<'_>

A new surface over the same grid, area, and clip, but writing to layer instead.

Source

pub const fn on_tier(&mut self, tier: Layer) -> Surface<'_>

Equivalent to self.on_layer(tier.as_u8()), for switching to one of the workspace’s named Layer tiers instead of a raw layer id. See Layer’s docs for when to reach for this over a numeric Surface::on_layer call.

Source

pub const fn tint(&self) -> Tint

The tint every sprite drawn through this surface is recoloured by.

Source

pub const fn origin(&self) -> (i32, i32)

The offset translate has accumulated on this surface, (0, 0) if it has never been called.

Every coordinate a caller passes to a coordinate-taking method has this subtracted from it before the usual bounds check (see translate’s doc), so a callee handed a &mut Surface can use this to tell whether it is in a translated coordinate space, compose a further offset relative to the current one without over- or undershooting, or convert a local coordinate it read back off the surface into the caller’s own coordinate space by adding this back in.

Source

pub const fn with_tint(&mut self, tint: Tint) -> Surface<'_>

A new surface over the same grid, area, and layer, recolouring every sprite it draws by tint.

Substituted rather than combined: unlike clip, which can only narrow, a tint replaces whatever the parent surface carried. Two tints do not compose into a third meaningful one, and silently multiplying an inherited shadow into a caller’s damage flash would be harder to predict than replacing it.

Applies to sprites only. A cell backend has no sprite to recolour and draws the cell’s glyph in its own Style, tinted or not, so this is invisible there. See Tint.

This tint composes with the sheet’s own colour treatment; see retroglyph_window::tileset::SheetColor and retroglyph_window::sprite_cache::SpriteTint for the two-stage resolution (retroglyph-core has no dependency on retroglyph-window, so these are plain names, not intra-doc links).

For a multi-cell span the tint lands on the anchor cell, which is where a pixel backend draws the sprite from. blit has no such anchor (grid is arbitrary composed content, not one sprite) and does not apply this tint at all; see its own doc.

§Examples
use retroglyph_core::color::{Style, Tint};
use retroglyph_core::grid::{Grid, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(8, 4);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 8, 4), 0);

// One grass sprite, drawn twice: once as itself, once dimmed into shadow.
let grass = '\u{E000}';
surface.put_span_uniform((0, 0), (2, 1), grass, ' ', Style::default())?;
surface
    .with_tint(Tint::multiply(128, 128, 128))
    .put_span_uniform((2, 0), (2, 1), grass, ' ', Style::default())?;

assert_eq!(grid.tint(0, 0, 0), Tint::None);
assert_eq!(grid.tint(0, 2, 0), Tint::multiply(128, 128, 128));
Source

pub fn clip(&mut self, rect: Rect) -> Surface<'_>

A new surface over the same grid, layer, and area, whose clip_rect is narrowed to rect intersected with this surface’s own clip. What this surface represents is unchanged; only what is visible shrinks.

rect is in absolute grid coordinates (it intersects clip_rect, itself absolute), not local to this surface’s own area the way fill_rect, clear_region, and print_aligned’s own rect are. Coordinates are otherwise unchanged: the sub-surface addresses the same space this one does, so a sub-rect computed against Surface::area (e.g. by a layout split) can be passed straight in. Because the clip is intersected rather than substituted, narrowing is monotonic: handing a surface down a layout tree can only ever tighten what a callee is able to draw into, never widen it.

Clipping is also how the clip-sensitive calls are told what they are drawing into:

  • print wraps overflow onto the next row. Clipped to a one-row bar, the wrapped remainder falls outside the clip and is dropped, which is what a single-line bar wants.
  • put_span and put_span_uniform refuse a footprint that leaves the clip. Clipped to a content rect, “fits” stops meaning “fits the screen” and starts meaning “does not reserve cells in the status bar below”.

A sub-surface that should instead represent rect (e.g. a widget’s own region, laid out and centered against rect rather than the parent’s wider area) wants scope, not this.

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Pos, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(6, 2);
let mut screen = Surface::new(&mut grid, Rect::new(0, 0, 6, 2), 0);

// A title too long for the one-row bar at the top: the remainder wraps out of the
// clip instead of onto the map below.
screen
    .clip(Rect::new(0, 0, 6, 1))
    .print((0, 0), "retroglyph", Style::default());

assert_eq!(grid[Pos::new(0, 0)].glyph(), 'r');
assert_eq!(grid[Pos::new(0, 1)].glyph(), ' ');
Source

pub fn scope(&mut self, rect: Rect) -> Surface<'_>

A new surface over the same grid and layer, that represents rect: its area becomes rect, and its clip_rect is narrowed to rect intersected with this surface’s own clip.

This is the primitive a widget’s own region is built from: a sub-widget laid out against rect should center, align, and measure itself against rect (via area), while still being unable to draw outside whatever was already visible in the parent. A clip alone cannot do this, because clip leaves area untouched; scope is what a caller reaches for when handing a sub-rect down to something that is going to read that rect back off the surface.

Like clip, the clip narrows monotonically: a rect that reaches outside this surface’s own clip only ever tightens what the returned surface can draw into, never widens it, even though area itself becomes exactly rect.

§Examples
use retroglyph_core::grid::{Grid, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(8, 4);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 8, 4), 0);

let mut clipped = surface.clip(Rect::new(0, 0, 4, 4));
// `scope` widens `area` to a rect the parent's clip does not fully cover...
let scoped = clipped.scope(Rect::new(2, 0, 4, 4));
assert_eq!(scoped.area(), Rect::new(2, 0, 4, 4));
// ...but the visible region still cannot exceed the parent's own clip.
assert_eq!(scoped.clip_rect(), Rect::new(2, 0, 2, 4));
Source

pub const fn translate(&mut self, origin: (i32, i32)) -> Surface<'_>

A view whose (0, 0) sits at origin relative to this surface’s own coordinate space, so a caller can draw in a shifted (e.g. world/camera) coordinate space and let the surface do the clipping, rather than subtracting origin from every coordinate by hand.

Every coordinate-taking method on the returned surface (put, put_signed, print, print_line, fill_rect, put_offset, put_span, put_span_uniform, and clear_region) subtracts origin (composed with any outstanding translate) from the coordinate it is given before applying its usual bounds check. Only clear, which takes no coordinate and always clears this surface’s whole area, is unaffected.

This does not touch area or clip_rect, so both, along with width and height, keep reporting the same thing before and after translating: only the coordinate a caller must pass to land a write shifts, never what the surface itself covers or what is visible in it. This composes with scope the same order it is called in: scope(...).translate(...) first narrows the area and clip, then shifts the coordinate space that still-narrowed area is addressed in, so a coordinate that goes negative after the shift can land inside the pre-narrowed area.

The offset accumulates with saturating arithmetic: chaining enough translate calls in one direction clamps origin at i32::MIN/i32::MAX instead of wrapping or panicking. A coordinate that only lands after an offset larger than that was never addressable in this surface’s u16 grid space to begin with.

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Pos, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(10, 10);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0);

// Narrow to a 4x4 viewport, then shift its coordinate space by (-5, -5): translating
// does not move or resize the viewport itself.
let mut scoped = surface.scope(Rect::new(5, 5, 4, 4));
let mut view = scoped.translate((-5, -5));
assert_eq!(view.area(), Rect::new(5, 5, 4, 4));

// (-5, -5) minus the translate offset (-5, -5) is (0, 0): the viewport's own local
// origin, which lands at the viewport's top-left grid cell (5, 5).
view.put_signed((-5, -5), 'X', Style::default());

assert_eq!(grid[Pos::new(5, 5)].glyph(), 'X');
Source

pub fn clip_translate(&mut self, area: Rect, origin: (i32, i32)) -> Surface<'_>

clip to area, then translate by origin, in one call – except that unlike plain clip, the returned surface’s area is area intersected with this surface’s own area, not area verbatim.

Chaining clip(...).translate(...) directly works when the result is used right where it’s produced (both clip and translate return a Surface<'_> borrowing the previous step for exactly that call), but a helper that hands the composed view back to its own caller (for example a scrolling-camera widget’s own surface method) needs the two narrowings applied against a single &mut self borrow instead, so the returned surface can outlive the call. This does that.

This intersects area with this surface’s own area rather than replacing it the way scope does, so area/width/ height on the result can report something smaller than the area argument. A scrolling-camera widget’s surface method relies on exactly this: when the world is smaller than the viewport, it hands in a viewport-sized area and depends on the intersection to shrink it back down to the world’s own size. A caller that wants area to become exactly its argument, even reaching outside the parent’s current area, should use scope followed by translate instead.

The offset accumulates with saturating arithmetic, as in translate.

§Examples
use retroglyph_core::color::Style;
use retroglyph_core::grid::{Grid, Pos, Rect};
use retroglyph_core::surface::Surface;

let mut grid = Grid::new(10, 10);
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0);

let mut view = surface.clip_translate(Rect::new(5, 5, 4, 4), (-5, -5));
assert_eq!(view.area(), Rect::new(5, 5, 4, 4));

view.put_signed((-5, -5), 'X', Style::default());
assert_eq!(grid[Pos::new(5, 5)].glyph(), 'X');
Source

pub const fn with_style(&mut self, style: Style) -> StyledSurface<'_, 'a>

A styled view over this surface: same area and layer, but every draw call uses style without needing to pass it each time. Handy for a run of same-styled writes (e.g. filling in a wall glyph over many cells) without repeating the Style at every call site.

Source

pub const fn grid_mut(&mut self) -> &mut Grid

Borrows the underlying Grid directly, with no clipping.

Escape hatch for multi-layer or whole-grid operations (e.g. Grid::blit) that don’t fit this surface’s clipped, single-layer model. Drawing into a sub-rect is not one of those: clip and scope narrow a surface without handing out the unclipped grid to do it.

Source

pub const fn grid(&self) -> &Grid

Read-only counterpart of grid_mut.

Source§

impl<'a> Surface<'a>

Source

pub const fn new(grid: &'a mut Grid, area: Rect, layer: u8) -> Self

A surface over grid, scoped to area on layer, tinting nothing. area starts out fully visible: area and clip_rect are equal until clip or scope narrows the latter.

Auto Trait Implementations§

§

impl<'a> Freeze for Surface<'a>

§

impl<'a> RefUnwindSafe for Surface<'a>

§

impl<'a> Send for Surface<'a>

§

impl<'a> Sync for Surface<'a>

§

impl<'a> Unpin for Surface<'a>

§

impl<'a> UnsafeUnpin for Surface<'a>

§

impl<'a> !UnwindSafe for Surface<'a>

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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.