Skip to content

Tutorial States

ddbnl edited this page Aug 30, 2022 · 1 revision

Basics

Before we go on to describe the scheduler, we have to look at widget states first. We said that the scheduler was how we manage the UI from code, and we do that by manipulating widget states. Every property of a widget (such as those we set from the .ez files) is contained in its widget state. If we change a property in the widget state and call the '.update' method on it, the widget will be redrawn on the next frame to reflect the new state. So if we wanted to change the text of a label from code it would look like this:

label_state.set_text("new text".to_string());

The state of every widget active in our UI is contained in the "State tree". The state tree is available to us when we initialize the UI, and is given to us in every callback. We can use the state tree to get a widget state using the "get" or "get_mut" methods. So if we wanted to change the text of a label with the id "my_label" when initializing the UI, we would do this:

use ez_term::*;
let (root_widget, mut state_tree, mut scheduler) = load_ui();

let label_state = state_tree.get_mut("my_label").as_label_mut();
label_state.set_text("new text".to_string());

run(root_widget, state_tree, scheduler);

Casting to the right state type

Note that after getting the state of the label, we have to call the "to_label_mut" method to change the state into a label state. This is because the state tree contains generic states (due to Rusts strict type requirements), and so we have to cast the state into the right type before we can actually use it. This will become second nature quickly when working with EzTerm. Just keep in mind: if you want to actually alter the property of a state, call "to_x" or "to_x_mut" on it first. Here are all available casts:

  • as_layout(_mut)
  • as_label(_mut)
  • as_text_input(_mut)
  • as_button(_mut)
  • as_checkbox(_mut)
  • as_radio_button(_mut)
  • as_slider(_mut)
  • as_dropdown(_mut)
  • as_progress_bar(_mut)
  • as_canvas(_mut)

Retrieving states

The 'state_tree' object is actually the root layout state. So if you ever need the root layout state, you would get it like this:

let root_layout_state = state_tree.as_layout();

If we want any other states, we have to use the "get" or "get_mut" methods to find them. In the earlier example we used a widget ID for this. There are in fact three ways to find states in the state tree: by ID, by path or by chaining 'get' calls. Let's look at examples of all three methods:

By id:

let label_state = state_tree.get("my_label").as_label();

Important note: to find widgets by ID, the ID must be unique from that point in the tree. That means that if you search an ID from the root of the state tree, that the ID must be globally unique! As a general rule, make all IDs in your .ez files globally unique if at all possible, because finding states by ID is the most comfortable way to do it, and incurs no performance penalty due to caching.

By path:

let label_state = state_tree.get("/root/layout/sub_layout/my_label").as_label();

Here we used the full path to find a widget. A true full path always starts with "/root" (the static name of the root layout), but since "state_tree" is in fact the root itself, we do not necessarily have to start our paths with "/root", so we could use the shorter version:

let label_state = state_tree.get("/layout/sub_layout/my_label").as_label();

This is still a bit verbose, so it's usually more convenient to make IDs globally unique and search by ID.

By chaining get calls:

The last method is to chain get calls. The 'get' method returns another part of the state tree, so we could just call 'get' again:

let label_state = state_tree.get("layout").get("sub_layout").get("my_label").as_label();

This is very verbose; so when is this useful? Mostly you want to avoid it, but it comes in handy when you want to manipulate multiple child states with non-unique IDs. Let's say you have layouts with three labels, and you want to update the text of each label. You could retrieve the layout state first, and then access each child state from there:

let layout_1 = state_tree.get("sub_layout_1");
layout_1.get("my_label_1").as_label_mut().set_text("Some".to_string());
layout_1.get("my_label_2").as_label_mut().set_text("new".to_string());
layout_1.get("my_label_3").as_label_mut().set_text("Text".to_string());
let layout_2 = state_tree.get("sub_layout_2");
layout_2.get("my_label_1").as_label_mut().set_text("Some".to_string());
layout_2.get("my_label_2").as_label_mut().set_text("new".to_string());
layout_2.get("my_label_3").as_label_mut().set_text("Text".to_string());

StateTree in callbacks and tasks

We will describe callbacks in their own page, but we will note for now that the state tree is available in callbacks through the "context" parameter. So if we wanted to change the text of our label from a callback it would look like this:

use ez_term::*;
fn my_callback(context: Context) -> bool {

    let label_state = context.state_tree.get_mut("my_label").as_label_mut();
    label_state.set_text("new text".to_string());
    label_state.update(context.scheduler);
    true
}

Don't worry about the callback syntax for now, just note that we control widgets from code by manipulating the state, which we get from the state tree available in the callback context. We could also see in the example that each state has an ".update" method. When we call update, the widget will be redrawn on the next frame. You will want to call this when changing a state from a callback most of the time.

Now we'll look at the scheduler object. After that, we will start putting our new knowledge about states to use when discussing the actual features of the scheduler.

Continue

The general tutorial continues with: The Scheduler Object.

Tutorial Tutorial-Project-Structure
  Minimal example
EzLang
  EzLang basics
  EzLang Templates
  Ezlang Layout modes
   EzLang Box mode layouts
   EzLang Stack mode layouts
   EzLang Table mode layouts
   EzLang Float mode layouts
   EzLang Tab mode layouts
   EzLang Screen mode layouts
   EzLang Layout Scrolling
   EzLang Layout Views
  EzLang Widget overview
   EzLang Label
   EzLang Text Input
   EzLang Button
   EzLang Checkbox
   EzLang Radio button
   EzLang Dropdown
   EzLang Slider
   EzLang Canvas
  EzLang Property Binding
  EzLang Sizing
   EzLang Size hints
   EzLang Auto scaling
   EzLang Maths Sizing
   EzLang Manual Sizing
  EzLang Positioning
   EzLang Layout Mode Positioning
   EzLang Position Hints
   EzLang Position Maths
   EzLang Manual Position
   EzLang Adjusting Position
  EzLang Keyboard Selection
Scheduler
  Widget States and the State Tree
  The Scheduler Object
  Managing callbacks
   Callback Structure
   Callback Configs
   Callback: On keyboard enter
   Callback: On Left Mouse Click
   Callback: On Press
   Callback: On Select
   Callback: On Deselect
   Callback: On Right Mouse Click
   Callback: On Hover
   Callback: On Drag
   Callback: On Scroll Up
   Callback: On Scroll Down
   Callback: On Value Change
   Callback: Custom Key Binds
   Callback: Global Key Binds
   Callback: Property Binds
  Tasks
   Scheduled Single Exectution Tasks
   Scheduled Recurring Tasks
   Threaded Tasks
  Custom Properties
  Modals
  Programmatic Widgets
  Updating widgets
  Managing selection
Default global (key)binds
Performance
ExamplesLayout: Box Mode Nested
Layout: Box Mode Size Hints
Layout: Stack Mode
Layout: Table Mode Dynamic
Layout: Table Mode Static
Layout: Float Mode Manual
Layout: Float Mode Position hints
Layout: Screen Mode
Layout: Tab Mode
Layout: Scrolling
Layout: Views
Widget: Label
Widget: Text input
Widget: Button
Widget: Checkbox
Widget: Radio Button
Widget: Dropdown
Widget: Slider
Widget: Progress Bar
Widget: Canvas
Scheduler: Schedule Once
Scheduler: Schedule Once Callback
Scheduler: Schedule Recurring
Scheduler: Schedule Recurring Callback
Scheduler: Threaded Task State Tree
Scheduler: Threaded Task Custom Property
Scheduler: Create Widgets
Scheduler: Modal Popup
Reference Widgets
  Common Properties
  Label
  Text Input
  Button
  Checkbox
  Radio button
  Dropdown
  Slider
  Canvas
Scheduler
  Schedule once
  Schedule Recurring
  Schedule Threaded
  Cancel Task
  Cancel Recurring Task
  Create Widget
  Remove Widget
  Select Widget
  Deselect Widget
  Update Widget
  Force Redraw
  Open Modal
  Dismiss Modal
  Bind Global Key
  Remove Global Key
  Clear Global Keys
  Bind Property
  Create Custom Properties
  Get Property
  Get Property Mut
  Overwrite Callback Config
  Update Callback Config
  Exit
Clone this wiki locally