Text

Text widgets are those that implement the DisplayExt. There are 3 and these can be found in the text module:

  • TextDisplay
  • TextEditor
  • SimpleTerminal

The main purpose of these widgets is displaying/editing text. The first 2 widgets require a TextBuffer, while the SimpleTerminal has an internal buffer:

use fltk::{prelude::*, *};

fn main() {
    let a = app::App::default();
    let mut buf = text::TextBuffer::default();

    let mut win = window::Window::default().with_size(400, 300);
    let mut txt = text::TextEditor::default().with_size(390, 290).center_of_parent();
    txt.set_buffer(buf.clone());
    win.end();
    win.show();

    buf.set_text("Hello world!");
    buf.append("\n");
    buf.append("This is a text editor!");

    a.run().unwrap();
}

image

Most operations are done through the TextBuffer. Text can be appended using append() or the whole content can be set using set_text(). You can get back a clone (reference type) of the buffer using the DisplayExt::buffer() method:

use fltk::{prelude::*, *};

fn main() {
    let a = app::App::default();
    let buf = text::TextBuffer::default();

    let mut win = window::Window::default().with_size(400, 300);
    let mut txt = text::TextEditor::default().with_size(390, 290).center_of_parent();
    txt.set_buffer(buf);
    win.end();
    win.show();

    let mut my_buf = txt.buffer().unwrap();

    my_buf.set_text("Hello world!");
    my_buf.append("\n");
    my_buf.append("This is a text editor!");

    a.run().unwrap();
}

The DisplayExt offers other methods to manage the text properties such as wrapping, cursor position, font, color, size...etc:

use fltk::{enums::Color, prelude::*, *};

fn main() {
    let a = app::App::default();
    let mut buf = text::TextBuffer::default();
    buf.set_text("Hello world!");
    buf.append("\n");
    buf.append("This is a text editor!");

    let mut win = window::Window::default().with_size(400, 300);
    let mut txt = text::TextDisplay::default().with_size(390, 290).center_of_parent();
    txt.set_buffer(buf);
    txt.wrap_mode(text::WrapMode::AtBounds, 0); // bounds don't require the second argument, unlike AtPixel and AtColumn
    txt.set_text_color(Color::Red);
    win.end();
    win.show();

    a.run().unwrap();
}

image

The TextBuffer has also a second purpose, and that's to provide a style buffer. A style buffer mirrors your text buffer and uses a style table (containing font, color and size) to add granular styling to your text, the style table itself is indexed, so to speak, using the corresponding letter:

use fltk::{
    enums::{Color, Font},
    prelude::*,
    *,
};

const STYLES: &[text::StyleTableEntry] = &[
    text::StyleTableEntry {
        color: Color::Green,
        font: Font::Courier,
        size: 16,
    },
    text::StyleTableEntry {
        color: Color::Red,
        font: Font::Courier,
        size: 16,
    },
    text::StyleTableEntry {
        color: Color::from_u32(0x8000ff),
        font: Font::Courier,
        size: 16,
    },
];

fn main() {
    let a = app::App::default();
    let mut buf = text::TextBuffer::default();
    let mut sbuf = text::TextBuffer::default();
    buf.set_text("Hello world!");
    sbuf.set_text(&"A".repeat("Hello world!".len())); // A represents the first entry in the table, repeated for every letter
    buf.append("\n"); 
    sbuf.append("B"); // Although a new line and the style might not apply, but it's needed to avoid messing out subsequent entries
    buf.append("This is a text editor!");
    sbuf.append(&"C".repeat("This is a text editor!".len()));

    let mut win = window::Window::default().with_size(400, 300);
    let mut txt = text::TextDisplay::default()
        .with_size(390, 290)
        .center_of_parent();
    txt.set_buffer(buf);
    txt.set_highlight_data(sbuf, STYLES.to_vec());
    win.end();
    win.show();

    a.run().unwrap();
}

image

The terminal example uses the SimpleTerminal along with a style TextBuffer. It can be found here