Module intuitive::components

source ·
Expand description

A collection of basic components.

This module contains two main things:

Components

Components are the main building blocks of Intuitive TUIs. Most components can be built using the component attribute macro. For more complex components, such as those that require special handling when drawing, consider implementing the Component trait directly.

Recipes

The examples below are a few recipes for commonly constructed components. Also be sure to refer to the examples directory in the repository. These recipes exclude the use statements in order to shorten the code samples.

Input Box

An input box with state can easily be created with a functional component:

#[component(Input)]
fn render(title: String) {
  let text = use_state(|| String::new());
  let on_key = on_key! { [text]
    KeyEvent { code: Char(c), .. } => text.mutate(|text| text.push(c)),
    KeyEvent { code: Backspace, .. } => text.mutate(|text| text.pop()),
  };

  render! {
    Section(title) {
      Text(text: text.get(), on_key)
    }
  }
}

Input Box With Cursor

Drawing a cursor requires us to implement a custom Element, specifically so we can control the drawing of the cursor. Notice that we use a functional component to return a custom element::Any, instead of returning a render! invocation.

#[component(Input)]
fn render(title: String) {
  let text = use_state(|| String::new());

  let on_key = on_key! { [text]
    KeyEvent { code: Char(c), .. } => text.mutate(|text| text.push(c)),
    KeyEvent { code: Backspace, .. } => text.mutate(|text| text.pop()),
  };

  AnyElement::new(Frozen {
    cursor: text.get().len() as u16,
    content: render! {
      Section(title: title.clone(), on_key) {
        Text(text: text.get())
      }
    },
  })
}

struct Frozen {
  cursor: u16,
  content: AnyElement,
}

impl Element for Frozen {
  fn on_key(&self, event: KeyEvent) {
    self.content.on_key(event);
  }

  fn draw(&self, rect: Rect, frame: &mut Frame) {
    self.content.draw(rect, frame);
    frame.set_cursor(rect.x + self.cursor + 1, rect.y + 1);
  }
}

Focus

In order to implement focusing on specific sections, we need to construct the components to be focused on, specifically the three Inputs manually, when rendering our Root component. Notice that we also call Component::render on those Inputs, because we want to be able to delegate key events to them, depending on which is focused. Lastly, we use Embed in order to make use of a rendered component inside of the render! macro.

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Focus {
  A,
  B,
  C,
}

#[component(Input)]
fn render(title: String, focused: bool) {
  let text = use_state(|| String::new());

  let color = if *focused { Color::Blue } else { Color::Gray };

  let on_key = on_key! { [text]
    KeyEvent { code: Char(c), .. } => text.mutate(|text| text.push(c)),
    KeyEvent { code: Backspace, .. } => text.mutate(|text| text.pop()),
  };

  render! {
    Section(title, border: color) {
      Text(text: text.get(), on_key)
    }
  }
}

#[component(Root)]
fn render() {
  let focus = use_state(|| Focus::A);

  let input_a = Input::new("A".to_string(), focus.get() == Focus::A).render();
  let input_b = Input::new("B".to_string(), focus.get() == Focus::B).render();
  let input_c = Input::new("C".to_string(), focus.get() == Focus::C).render();

  let on_key = on_key! { [focus, input_a, input_b, input_c]
    KeyEvent { code: Tab, .. } => focus.update(|focus| match focus {
      Focus::A => Focus::B,
      Focus::B => Focus::C,
      Focus::C => Focus::A,
    }),

    event if focus.get() == Focus::A => input_a.on_key(event),
    event if focus.get() == Focus::B => input_b.on_key(event),
    event if focus.get() == Focus::C => input_c.on_key(event),

    KeyEvent { code: Esc, .. } => event::quit(),
  };

  render! {
    VStack(on_key) {
      Embed(content: input_a)
      Embed(content: input_b)
      Embed(content: input_c)
    }
  }
}

Modules

  • Structures for dealing with child components.
  • experimentalexperimental
    An experimental collection of components not subject to semver.
  • Structures relating to the HStack and VStack components.

Structs

  • An opaque type holding a struct that implements Component.
  • A component for centering its contents.
  • A component that renders an element::Any or a component::Any.
  • A component that renders nothing.
  • A component that for renders a horizontal stack of components.
  • A component with a border and a title.
  • A component that displays text.
  • A component that renders a vertical stack of components.

Traits