3 releases (breaking)

0.3.0 Jan 4, 2022
0.2.0 Dec 27, 2021
0.1.0 Dec 26, 2021

36 downloads per month
Used in 3 crates

MIT/Apache

14KB
296 lines

Logo
An advanced, reactive UI library for Rust
Report a Bug · Request a Feature . Ask a Question


What is agui?

Agui is an advanced reactive GUI project for Rust, inspired by Flutter and taking some concepts from other related UI systems.

WARNING

Agui is very much still in heavy active development. The API will likely change, and it has yet to go under rigorous testing. However, that's not to say it's not ready for moderate use.

🛠️ Installation

Agui is available on crates.io, Rust's official package repository. Just add this to your Cargo.toml file:

[dependencies]
agui = "0.3" # ensure this is the latest version

🚀 Usage

Docs for agui are under development, however you can check the agui_wgpu/examples directory for basic setup, and agui_widgets for many examples on widget creation.

Creating new widgets

Currently, widgets are created using a StatelessWidget or StatefulWidget derive macro, and by implementing the WidgetView trait.

#[derive(Default, StatelessWidget)]
pub struct MyWidget {
    // We can define parameters, here.
    pub layout: Layout,

    // WidgetRef is the convention for passing children. Vec<WidgetRef> should be used for passing variable amounts.
    pub child: WidgetRef,
}

impl WidgetView for MyWidget {
    // Widgets can return nothing, one or more children, or an error. BuildResult is the enum we use to cover those possibilities.
    fn build(&self, ctx: &mut BuildContext) -> BuildResult {
        // `ctx.set_layout_type` is what we use to define this widget's layout type (row, column, grid).
        ctx.set_layout_type(LayoutType::Row);

        // `ctx.set_layout` is what we use to define this widget's layout parameters.
        ctx.set_layout(Layout::clone(&self.layout));

        build! {
            Button { }
        }
    }
}

What's build!?

The build! macro makes it significantly cleaner and easier to init new widgets. All it does is initialize unset fields in a struct to their Default::default(), and add .into() to the struct itself.

// It allows us to turn this:

fn build(&self, ctx: &mut BuildContext) -> BuildResult {
    BuildResult::Some(
        Button {
            layout: Layout::default(),
            color: Color::default(),
            child: Text {
                text: String::from("A Button")
            }
        }
    )
}

// Into this:

use agui::macros::build;

fn build(&self, ctx: &mut BuildContext) -> BuildResult {
    build!{
        Button {
            child: Text {
                text: "A Button"
            }
        }
    }
}

A more complex widget implementation (featuring globals and computed values) can be seen in the Button widget.

Functional widgets

Functional widgets are an additional quality-of-life magic way of creating new widgets. Since widgets are generally just fields with a build function, we can usually use a single function which represents the build function.

#[functional_widget]
fn example_widget(ctx: &BuildContext, layout: Layout, child: WidgetRef) -> BuildResult {
    ctx.set_layout(layout);

    build!{
        Button {
            child: Text {
                text: "A Button"
            }
        }
    }
}

The ctx: &BuildContext parameter is required, and any following arguments are added as a struct field.

How does it work?

There is a bit of magic going on, here. Put simply, any field used here must implement Default + Clone in some form or another, so that the widget may call the example_widget function without issue. Secondly, the generated widget struct will be named by converting snake_case to PascalCase, in this case: ExampleWidget.

Note that the necessity of .clone() will make this method of creating widgets slightly less efficient in some cases.

🤝 Contributing

Contributions are encouraged, and very welcome. Feel free to check the issues page if you wish to do so!

Please go through existing issues and pull requests to check if somebody else is already working on it. Also, make sure to run cargo test before you commit your changes!

Dependencies

~1–1.4MB
~33K SLoC