ColumnDef API

ColumnDef

pub struct ColumnDef {
    pub key: String,
    pub label: String,
    pub width: f64,
    pub format: Option<CellFormat>,
    pub editor: Option<CellEditor>,
    pub validator: Option<CellValidator>,
    pub rules: Vec<ValidationRule>,
}
FieldTypeDescription
keyStringUnique identifier, used to look up cell values
labelStringDisplay text in the column header
widthf64Width in logical pixels
formatOption<CellFormat>Display format (None = raw text)
editorOption<CellEditor>Editor type (None = default text input)
validatorOption<CellValidator>Legacy validator, kept for backward compatibility. Prefer rules for new code — see Validation
rulesVec<ValidationRule>Declarative validation rules, checked in order before validator

Constructor

pub fn new(key: impl Into<String>, label: impl Into<String>, width: f64) -> Self

Creates a column with no format and no editor override.

CellFormat

#[non_exhaustive]
pub enum CellFormat {
    Number { decimal_places: u8, thousands_sep: Option<char>, decimal_sep: char },
    Percent { decimal_places: u8 },
    Currency { symbol: String, decimal_places: u8, thousands_sep: Option<char>, symbol_after: bool },
    Boolean { true_label: String, false_label: String },
    Custom(Rc<dyn Fn(&str) -> FormattedCell>),
    Image { base_url: Option<String>, border_radius: f64, padding: f64 },
    ImageText { base_url: String, suffix: String, image_size: f64, border_radius: f64, gap: f64 },
}

Methods

MethodReturnsDescription
is_image()boolTrue for Image variant
is_image_text()boolTrue for ImageText variant

FormattedCell

pub struct FormattedCell {
    pub text: String,
    pub align: Option<CellAlign>,
    pub bold: bool,
    pub color: Option<[u8; 4]>,  // RGBA
}

CellAlign

pub enum CellAlign {
    Left,    // default
    Center,
    Right,
}

CellValidator

pub struct CellValidator(pub Rc<dyn Fn(&str) -> Result<(), String>>);

A per-column validation callback. Call CellValidator::new to construct one:

CellValidator::new(|v| {
    v.parse::<f64>()
        .map(|_| ())
        .map_err(|_| "not a number".to_string())
})
MethodDescription
new(f)Wrap a closure as a validator
validate(value: &str)Run the validator; returns Ok(()) or Err(message)

See Validation for the full feature guide.

ValidationRule

#[non_exhaustive]
pub enum ValidationRule {
    Required,
    MinLength(usize),
    MaxLength(usize),
    Range(f64, f64),
    OneOf(Vec<String>),
    Custom(CellValidator),
}

Declarative rules attached to ColumnDef::rules, checked in order, first failure wins, before the legacy validator.

Builder sugar on ColumnDef

MethodPushesReturns
.required()ValidationRule::RequiredSelf
.with_min_length(min: usize)ValidationRule::MinLength(min)Self
.with_max_length(max: usize)ValidationRule::MaxLength(max)Self
.with_range(min: f64, max: f64)ValidationRule::Range(min, max)Self
.with_allowed_values(values: Vec<String>)ValidationRule::OneOf(values)Self
.with_rules(rules: Vec<ValidationRule>)replaces rules entirelySelf

ColumnDef::validate_value

pub fn validate_value(&self, value: &str) -> Result<(), String>

Runs rules in order, then validator if all rules passed. Returns the first failure's message, if any.

See Validation for InvalidEditMode and live per-keystroke feedback.

RowPredicate<T>

pub struct RowPredicate<T>(pub Rc<dyn Fn(u64, &GridModel) -> T>);

Generic per-row callback wrapper backing EditablePredicate, CellDecorator, and CellButtonsVisible below — each is a type alias over RowPredicate<T> for a different T, not a separate implementation. EditablePredicate and CellButtonsVisible are both RowPredicate<bool>, so they're the literal same type (and print identical Debug output); CellDecorator is RowPredicate<Option<CellDecoration>>.

MethodDescription
new(f)Wrap a closure as a callback
evaluate(row: u64, model: &GridModel)Run the callback, returning T

EditablePredicate

pub type EditablePredicate = RowPredicate<bool>;

A dynamic per-cell editability override, attached to ColumnDef.editable_predicate. Receives the row index and the full GridModel, so it can implement cross-column logic (e.g. lock a cell when another column's value is "locked") — not just this column's own value.

EditablePredicate::new(|row, model| {
    model.get_cell(row, "status").as_deref() != Some("locked")
})

See RowPredicate<T> above for new/evaluate.

Builder sugar on ColumnDef

MethodSetsReturns
.editable_when(f)editable_predicateSelf

ColumnDef::is_cell_editable

pub fn is_cell_editable(&self, row: u64, model: &GridModel) -> bool

Resolves whether row is editable in this column: false if the static editable flag is false (the predicate is not even called); otherwise the predicate's result, or true if no predicate is set.

See Per-cell editability for the full feature guide, including the not-allowed cursor and themed locked-cell styling.

CellDecoration

#[non_exhaustive]
pub struct CellDecoration {
    pub border_color: Option<[u8; 4]>,
    pub background_tint: Option<[u8; 4]>,
    pub message: Option<String>,
}

A persistent, at-rest visual annotation for a single cell — a border color and/or background tint that stays visible whenever the cell is on screen, not just while it's being edited. Purely cosmetic: unlike validation, it never blocks a value from being committed, only changes how the cell is painted.

border_color/background_tint are RGBA ([r, g, b, a]), supplied directly by your closure — not read from the theme. Only the border's stroke width is themed (--rs-grid-decoration-border-width, default 1.5px), since it's uniform across every decorated cell regardless of which color you pick.

message is reserved for a future native hover tooltip — it is not rendered anywhere yet.

CellDecoration is #[non_exhaustive], so build one from CellDecoration::default() chained with the builders below rather than struct-literal syntax:

MethodSetsReturns
.with_border_color(rgba)border_colorSelf
.with_background_tint(rgba)background_tintSelf
.with_message(msg)messageSelf

CellDecorator

pub type CellDecorator = RowPredicate<Option<CellDecoration>>;

A dynamic per-cell decoration callback, attached to ColumnDef.decorator. Same RowPredicate<T> family as EditablePredicate (different T): receives the row index and the full GridModel, so it can implement cross-column logic — e.g. flag a cell when a paired column is inconsistent.

CellDecorator::new(|row, model| {
    let file = model.get_cell(row, "doc1_file").unwrap_or_default();
    let label = model.get_cell(row, "doc1_label").unwrap_or_default();
    (file.is_empty() != label.is_empty()).then(|| {
        CellDecoration::default().with_border_color([239, 68, 68, 255])
    })
})

See RowPredicate<T> above for new/evaluate.

Builder sugar on ColumnDef

MethodSetsReturns
.decorated_when(f)decoratorSelf

ColumnDef::cell_decoration

pub fn cell_decoration(&self, row: u64, model: &GridModel) -> Option<CellDecoration>

Resolves this cell's decoration, if any. Unlike is_cell_editable, there is no static gate — a decoration is purely cosmetic and not affected by ColumnDef::editable/GridModel::editable.

See Per-cell decoration for the full feature guide.

CellButtonsVisible

pub type CellButtonsVisible = RowPredicate<bool>;

A dynamic per-row visibility override for ColumnDef.cell_buttons, attached to ColumnDef.cell_buttons_visible. Same RowPredicate<T> family as EditablePredicate/CellDecorator — in fact the exact same concrete type as EditablePredicate (RowPredicate<bool>): receives the row index and the full GridModel, so it can implement cross-column logic — e.g. hide a button when a paired URL column is empty.

CellButtonsVisible::new(|row, model| {
    model.get_cell(row, "url").as_deref().is_some_and(|u| !u.is_empty())
})

See RowPredicate<T> above for new/evaluate.

Builder sugar on ColumnDef

MethodSetsReturns
.cell_buttons_visible_when(f)cell_buttons_visibleSelf

ColumnDef::are_cell_buttons_visible

pub fn are_cell_buttons_visible(&self, row: u64, model: &GridModel) -> bool

Resolves whether cell_buttons should be drawn for row. Like cell_decoration, there is no static gate — true whenever no predicate is set (today's behaviour).

See Cell Buttons for the full feature guide.

CellEditor

#[non_exhaustive]
pub enum CellEditor {
    Text,
    Select { options: Vec<SelectOption> },
}

SelectOption

pub struct SelectOption {
    pub value: String,
    pub label: String,
    pub icon: Option<String>,
}

ColumnOffsets

pub struct ColumnOffsets {
    pub offsets: Vec<f64>,
    pub total_width: f64,
}
MethodDescription
compute(columns: &[ColumnDef])Build offsets from column widths
hit_column(x: f64, columns: &[ColumnDef])Find column at x position