Skip to content

Primitives

Five types make up the foundation. Everything else in Textagram builds on them.

Cell

A cell holds one character. Empty cells are spaces.

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Cell {
    pub glyph: char,
}
textagram/src/grid.rs:1-4
impl Default for Cell {
    fn default() -> Self {
        Self { glyph: ' ' }
    }
}
textagram/src/grid.rs:12-16

Cursor

The cursor tracks position on the grid as (col, row). Column is x, row is y.

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Cursor {
    col: u16,
    row: u16,
}
textagram/src/grid.rs:18-22
    pub fn new() -> Self {
textagram/src/grid.rs:25-25
    pub fn position(&self) -> (u16, u16) {
textagram/src/grid.rs:29-29

Grid

The grid owns the canvas dimensions and the cursor. Movement clamps to bounds — the cursor never leaves the grid.

#[derive(Debug)]
pub struct Grid {
    width: u16,
    height: u16,
    cursor: Cursor,
}
textagram/src/grid.rs:48-53
    pub fn new(width: u16, height: u16) -> Self {
textagram/src/grid.rs:58-58
    pub fn move_cursor_by(&mut self, dx: i16, dy: i16) -> bool {
textagram/src/grid.rs:89-89

The clamping helper keeps arithmetic clean at the edges:

fn clamp_to_bounds(current: u16, delta: i16, limit: u16) -> u16 {
textagram/src/grid.rs:103-103

Direction and DirectionSet

Four cardinal directions. Each direction maps to a bit in a bitmask, so sets of directions pack into a single byte.

#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
pub enum Direction {
    North,
    East,
    South,
    West,
}
textagram/src/direction.rs:1-7
    pub const fn bit(self) -> u8 {
textagram/src/direction.rs:10-10
    pub const fn opposite(self) -> Self {
textagram/src/direction.rs:19-19
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DirectionSet {
    pub mask: u8,
}
textagram/src/direction.rs:29-32
    pub fn contains(&self, direction: Direction) -> bool {
textagram/src/direction.rs:47-47

Connectors use DirectionSet to decide which Unicode glyph to draw at each cell — a cell with North and East connections becomes , one with all four becomes .

LayerBuffer

The shared 2D storage. All three runtime layers — shapes, connectors, text — wrap a LayerBuffer.

#[derive(Clone, Debug)]
pub struct LayerBuffer {
    width: u16,
    height: u16,
    cells: Vec<char>,
}
textagram/src/layer.rs:1-6
    pub fn get_cell(&self, col: u16, row: u16) -> Option<char> {
textagram/src/layer.rs:37-37
    pub fn set_cell(&mut self, col: u16, row: u16, glyph: char) -> Option<char> {
textagram/src/layer.rs:53-53

set_cell returns Some(previous) only when the glyph actually changed. This drives dirty-tracking: if nothing changed, nothing redraws.