Struct tuig_ui::Region

source ·
pub struct Region<'s> { /* private fields */ }
Expand description

Something you can put an Attachment in.

You start with a Region that occupies the whole screen and captures one input. Then you split it to get more regions, and split those to get more, until you’ve got your whole layout. You can split a region with anything that implements Splitter, including the built-in ones in the “Implementors” section. When a region gets split, some inputs – notably mouse movements – will only get passed to the regions that they happen in.

In each region, you can put an Attachment, which is what tuig-ui calls UI elements. Attachments serve two purposes:

  • Handle user input in a way that makes sense for that element (e.g. a button returning true when it’s clicked)
  • Render the element to the screen (e.g. a button rendering its text and, when you click it, a fill color)

You can split regions and add attachments in whatever order you like, so e.g. you can change how you display bits depending on the state of user input, and refresh things on the same frame you get input.

Implementations§

source§

impl<'s> Region<'s>

source

pub fn new(screen: &'s mut Screen, input: Action) -> Self

Create a new Region encompassing an entire screen with a single input.

This is most typically called in your wrapper when you have input to handle, to create the root element you attach everything else into.

Examples found in repository?
examples/basic-tui.rs (line 51)
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
fn run(mut iosys: Box<dyn IoSystem>) {
    let mut ti = TextInput::new("> ", 5);
    let mut clicks = 0;
    let mut tui = |region: Region| {
        let [l, m, r] = region.split(cols!(20 "| |" * "#" 11)).unwrap();
        let [lt, lb] = l.split(rows!(* "=" 1)).unwrap();
        let [rt, rb] = r.split(rows!(1 "=" *)).unwrap();
        lt.attach(|i, sv| {
            let txt = text![
                "Hello! Your most recent ", red "action", " was: ",
                bold green "{:?}"(i),
            ];
            Textbox::new(txt).render_to(sv)
        });
        match lb.attach(&mut ti) {
            TextInputResult::Autocomplete { res, .. } => *res = "mlem!".into(),
            TextInputResult::Submit(line) => ti.store(line),
            _ => (),
        }
        m.attach(|i, mut sv: ScreenView| sv.fill(char_for_input(&i)));
        if rt.attach(Button("click me!").hotkey('4')) {
            clicks += 1;
        }
        rb.attach(Textbox::new(text!("{} clicks"(clicks))));
        true
    };

    let mut screen = Screen::new(iosys.size());
    let mut input = Action::Redraw;
    loop {
        screen.resize(iosys.size());
        let root = Region::new(&mut screen, input);
        if !tui(root) {
            break;
        }
        iosys.draw(&screen).expect("failed to render output");
        input = iosys.input().expect("failed to get input");
        if matches!(
            input,
            Action::Closed | Action::KeyPress { key: Key::Escape }
        ) {
            break;
        }
    }
    iosys.stop();
}
source

pub fn size(&self) -> XY

Get the size of this region of the screen.

source

pub fn split<S: Splitter<'s>>(self, splitter: S) -> S::Output

Split the region into one or more children.

The child regions never overlap each other, and never extend beyond the bounds of the parent. If you want to overlap, wait for #61 to be completed.

This consumes the parent and returns the child regions. It doesn’t modify anything in-place. If you don’t use the children, why even bother doing the split?

Examples found in repository?
examples/basic-tui.rs (line 24)
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
fn run(mut iosys: Box<dyn IoSystem>) {
    let mut ti = TextInput::new("> ", 5);
    let mut clicks = 0;
    let mut tui = |region: Region| {
        let [l, m, r] = region.split(cols!(20 "| |" * "#" 11)).unwrap();
        let [lt, lb] = l.split(rows!(* "=" 1)).unwrap();
        let [rt, rb] = r.split(rows!(1 "=" *)).unwrap();
        lt.attach(|i, sv| {
            let txt = text![
                "Hello! Your most recent ", red "action", " was: ",
                bold green "{:?}"(i),
            ];
            Textbox::new(txt).render_to(sv)
        });
        match lb.attach(&mut ti) {
            TextInputResult::Autocomplete { res, .. } => *res = "mlem!".into(),
            TextInputResult::Submit(line) => ti.store(line),
            _ => (),
        }
        m.attach(|i, mut sv: ScreenView| sv.fill(char_for_input(&i)));
        if rt.attach(Button("click me!").hotkey('4')) {
            clicks += 1;
        }
        rb.attach(Textbox::new(text!("{} clicks"(clicks))));
        true
    };

    let mut screen = Screen::new(iosys.size());
    let mut input = Action::Redraw;
    loop {
        screen.resize(iosys.size());
        let root = Region::new(&mut screen, input);
        if !tui(root) {
            break;
        }
        iosys.draw(&screen).expect("failed to render output");
        input = iosys.input().expect("failed to get input");
        if matches!(
            input,
            Action::Closed | Action::KeyPress { key: Key::Escape }
        ) {
            break;
        }
    }
    iosys.stop();
}
source

pub fn attach<A: Attachment<'s>>(self, attachment: A) -> A::Output

Attach something to this region, returning whatever it wants based on the input.

Remember that it’s common to implement Attachment for &T or &mut T, especially for elements that need to store state of some sort. If you’re getting weird errors about a type not implementing Attachment when you’re 100% sure it does, check the type’s docs and impl Attachment block more carefully.

Examples found in repository?
examples/basic-tui.rs (lines 27-33)
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
fn run(mut iosys: Box<dyn IoSystem>) {
    let mut ti = TextInput::new("> ", 5);
    let mut clicks = 0;
    let mut tui = |region: Region| {
        let [l, m, r] = region.split(cols!(20 "| |" * "#" 11)).unwrap();
        let [lt, lb] = l.split(rows!(* "=" 1)).unwrap();
        let [rt, rb] = r.split(rows!(1 "=" *)).unwrap();
        lt.attach(|i, sv| {
            let txt = text![
                "Hello! Your most recent ", red "action", " was: ",
                bold green "{:?}"(i),
            ];
            Textbox::new(txt).render_to(sv)
        });
        match lb.attach(&mut ti) {
            TextInputResult::Autocomplete { res, .. } => *res = "mlem!".into(),
            TextInputResult::Submit(line) => ti.store(line),
            _ => (),
        }
        m.attach(|i, mut sv: ScreenView| sv.fill(char_for_input(&i)));
        if rt.attach(Button("click me!").hotkey('4')) {
            clicks += 1;
        }
        rb.attach(Textbox::new(text!("{} clicks"(clicks))));
        true
    };

    let mut screen = Screen::new(iosys.size());
    let mut input = Action::Redraw;
    loop {
        screen.resize(iosys.size());
        let root = Region::new(&mut screen, input);
        if !tui(root) {
            break;
        }
        iosys.draw(&screen).expect("failed to render output");
        input = iosys.input().expect("failed to get input");
        if matches!(
            input,
            Action::Closed | Action::KeyPress { key: Key::Escape }
        ) {
            break;
        }
    }
    iosys.stop();
}
source

pub fn fill(self, cell: Cell)

Fill the whole region with (copies of) a cell.

source

pub fn text(self, text: Vec<Text>) -> TextboxData

Fill the whole region with some text.

If this runs out of space, it will cut off the bottom of the text. If you need more control over how it’s displayed, create and attach a Textbox directly.

Returns a TextboxData, as usual.

source§

impl Region<'static>

source

pub fn empty(input: Action) -> Self

Create an empty region taking any input.

This is able to have 'static lifetime because it’s not actually referring to any part of any screen. It has zero size; it doesn’t need any backing memory because any writes into that will just vanish regardless.

Trait Implementations§

source§

impl<'s> Debug for Region<'s>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<'s> RefUnwindSafe for Region<'s>

§

impl<'s> !Send for Region<'s>

§

impl<'s> !Sync for Region<'s>

§

impl<'s> Unpin for Region<'s>

§

impl<'s> UnwindSafe for Region<'s>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere 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 Twhere 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 Twhere U: Into<T>,

§

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 Twhere U: TryFrom<T>,

§

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.