Interactivity

Make your elements respond to clicks, hovers, and keyboard focus. Ply gives you two levels of control: inline state queries inside a children closure, and callback-based events on the element builder.

🔗Inline

Check ui.pressed(), ui.hovered() and ui.focused() inside a .children() closure, we explained this in the last section:

🔗Callback

MethodFires
.on_hover()every frame the pointer is over the element.
.on_press()once when the pointer goes down.
.on_release()once when the pointer comes back up.
.on_focus()once when the element gets focused.
.on_unfocus()once when the element gets unfocused.

It gives you the element's Id and (for the pointer ones) a PointerData with the pointer position:

ui.element().width(grow!()).height(fixed!(80.0))
  .background_color(0x262220)
  .on_hover(|id, pointer| {
    println!("Hovering {:?} at ({}, {})", id, pointer.position.x, pointer.position.y);
  })
  .on_press(|id, pointer| {
    println!("Pressed {:?} at ({}, {})", id, pointer.position.x, pointer.position.y);
  })
  .on_release(|id, pointer| {
    println!("Released {:?} at ({}, {})", id, pointer.position.x, pointer.position.y);
  })
  .on_focus(|id| {
    println!("Focused {:?}", id);
  })
  .on_unfocus(|id| {
    println!("Unfocused {:?}", id);
  })
  .empty();

🔗Pointer queries

As mentioned in Interactivity you can query state by ID from anywhere:

// All elements under the pointer, z-sorted
let hovered = ply.pointer_over_ids();

let mut ui = ply.begin();

if ui.is_pressed("submit") {
  // add active state "submit" element
} else {
  // add inactive state "submit" element
}

if ui.pointer_over("tooltip_trigger") {
  // show tooltip
}

🔗Preserve focus

Toolbar buttons that modify a text input shouldn't steal focus from the input. Use .preserve_focus():

ui.element().width(fixed!(32.0)).height(fixed!(32.0))
  .preserve_focus()
  .on_press(|_, _| { /* apply formatting */ })
  .children(|ui| {
    ui.text("Make red", |t| t.font_size(14).color(0xFFFFFF));
  });

Without .preserve_focus(), clicking the button would unfocus the text input, then the {color=red| wouldn't get applied.

🔗Building a button

Putting it all together:

fn button(ui: &mut Ui, label: &str, on_click: impl FnMut(Id) + 'static) {
  ui.element()
    .width(fit!())
    .height(fixed!(36.0))
    .corner_radius(6.0)
    .on_press(on_click)
    .children(|ui| {
      let bg = if ui.pressed() {
        0xB91414
      } else if ui.hovered() || ui.focused() {
        0xFF654D
      } else {
        0x3A3533
      };

      ui.element().width(fit!()).height(grow!())
        .background_color(bg)
        .corner_radius(6.0)
        .layout(|l| l.padding((0, 16, 0, 16)).align(CenterX, CenterY))
        .children(|ui| {
          ui.text(label, |t| t.font_size(14).color(0xFFFFFF));
        });
    });
}

Use it:

ui.element()
  .width(grow!())
  .height(grow!())
  .layout(|l| l.direction(LeftToRight).gap(8).padding(16).align(Left, Top))
  .children(|ui| {
    button(ui, "Save", |_| { println!("Saved!"); });
    button(ui, "Cancel", |_| { println!("Cancelled!"); });
    button(ui, "Delete", |_| { println!("Deleted!"); });
  });

🔗Next steps

Floating Elements