MorphStatusButton

A single button that morphs through idle, loading, success, and error states in place, with no layout shift.

Preview

Loading GPUI

Props

Variants

It fails sometimes

When the returned future resolves to Err, the button drops into its error state.

Loading GPUI
MorphStatusButton::new("deploy")
    .label("Deploy")
    .on_click_async(|_, _, cx| {
        let work = cx.background_executor().timer(Duration::from_millis(1200));
        async move {
            work.await;
            Err::<(), ()>(())
        }
    })

Install

cargo add evilbuttons

Usage

use evilbuttons::MorphStatusButton;
use gpui::{div, prelude::*, Context, IntoElement, Render, Window};

struct ButtonDemo;

impl Render for ButtonDemo {
    fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
        div().child(
            MorphStatusButton::new("save")
                .label("Save changes")
                .success_label("Saved")
                .on_click_async(|_, _, cx| {
                    let save = save_changes(cx);
                    async move { save.await }
                }),
        )
    }
}
MorphStatusButton::new("save")
    .label("Save changes")
    .status(if is_pending {
        MorphStatusButtonStatus::Loading
    } else {
        MorphStatusButtonStatus::Idle
    })

Props

MorphStatusButton is a GPUI RenderOnce builder; its status is keyed by id.

MethodTypeDefaultDescription
new(id)impl Into<ElementId>-Unique id for the button state.
label(s)impl Into<SharedString>"Save changes"Idle label.
loading_label(s)impl Into<SharedString>"Working…"Label shown while the action is in flight.
success_label(s)impl Into<SharedString>"Done"Label shown after the action succeeds.
error_label(s)impl Into<SharedString>"It broke. Your fault."Label shown after the action fails.
on_click_async(f)Fn(&ClickEvent, &mut Window, &mut App) -> impl Future<Output = Result<T, E>>-Async click handler. The returned future drives the auto state machine: loading, then success (Ok) or error (Err).
on_click(f)Fn(&ClickEvent, &mut Window, &mut App)-Plain click handler; doesn't change the status.
status(s)MorphStatusButtonStatus-Controlled status (Idle, Loading, Success, Error); disables the async auto-transitions when set.
reset_after(d)Duration1800msHow long to stay in success/error before resetting. Duration::ZERO stays.
disabled(bool)boolfalseDims the button and ignores input.

Good to know

  • Return a future from on_click_async and it shows loading, then success or error.
  • Clicks are ignored while loading, and the button stops reacting to hover and press.
  • The widest label reserves the width, so swapping labels never reflows the layout.

Search docs

Jump to a button documentation page