bezel

Components / Selection & input

Textarea

Markdown crates/ui/src/input.rs docs.rs

use ui::input::{Shape, TextField};

TextField::new(cx).with_shape(Shape::Rows(4))
TextField::new(cx).with_shape(Shape::Grow { min: 3, max: 12 })

Editing is identical across all three shapes — every action works on the content and a byte range. What the shape decides is the box.

Making enter mean something else

const COMPOSER: &str = "Composer";

cx.bind_keys([
    KeyBinding::new("enter", Send, Some(COMPOSER)),
    KeyBinding::new("shift-enter", input::InsertNewline, Some(COMPOSER)),
]);

let field = cx.new(|cx| {
    TextField::new(cx)
        .with_shape(Shape::Grow { min: 3, max: 12 })
        .with_key_context(COMPOSER)
});

Give that one box a context of its own. Rebinding the shared multi-line context would take the newline away from every other textarea in the app.

API

impl TextField {
    pub fn with_shape(self, shape: Shape) -> Self;

    /// Give one box a context of its own; rebinding the shared multi-line one
    /// would take the newline away from every other textarea in the app.
    pub fn with_key_context(self, context: impl Into<SharedString>) -> Self;

    // ...
}

pub enum Shape {
    /// The default. A pasted newline becomes a space rather than truncating
    /// the paste.
    Line,
    /// Exactly `n` lines, scrolling past that.
    Rows(usize),
    /// The composer shape: grows with the content, scrolls at `max`.
    Grow { min: usize, max: usize },
}

MULTILINE_KEY_CONTEXT is where enter, up and down are bound — not on every field, since a single-line field nested in a palette or combobox would win those keys and break list navigation.

home/ctrl-a goes to the start of the logical line, not of the visual row a soft wrap put you on — emacs' C-a, and a deliberate divergence from NSTextView.

Textarea