#widgets #ui #wgpu #interface

pixel-widgets

Component based UI library for graphical rust applications

24 releases (8 breaking)

0.9.1 Nov 7, 2021
0.8.0 Jun 17, 2021
0.5.9 Jan 1, 2021
0.5.8 Dec 31, 2020
0.3.0 Jul 28, 2020

#586 in GUI

28 downloads per month

MIT license

370KB
9K SLoC

Pixel-widgets

Documentation Crates.io License

pixel-widgets is a component based user interface library focused on integratability in graphical applications.

Features

  • Very compact and easy API
  • API agnostic rendering
  • Component based workflow
  • CSS like styling
  • Many built in widgets
  • wgpu based renderer included

Overview

User interfaces in pixel-widgets are composed of Components. These components manage their own state, and generate ui elements when that state is mutated. Each component implements some methods:

  • view - this method renders the ui elements for the current component state. When the state is updated, the view will be rendered again. method:
  • update - ui elements generate messages that will be passed to the update method. In here, a component will update it's internal state based on these messages.

Quick start

This example shows how to define a component and run it in the included sandbox. More work is required if you want to use pixel-widgets in your own game engine.

use pixel_widgets::prelude::*;

// The main component for our simple application
struct Counter {
    initial_value: i32,
}

Then, we have to define a message type. The message type should be able to tell us what happend in the ui.

// The message type that will be used in our `Counter` component.
#[derive(Clone)]
enum Message {
    UpPressed,
    DownPressed,
}

And finally, we must implement Component

use pixel_widgets::prelude::*;

// The main component for our simple application
#[derive(Default)]
struct Counter {
    initial_value: i32,
}

// The message type that will be used in our `Counter` component.
#[derive(Clone)]
enum Message {
    UpPressed,
    DownPressed,
}

impl Component for Counter {
    type State = i32;
    type Message = Message;
    type Output = ();

    // Creates the state of our component when it's first constructed.
    fn mount(&self) -> Self::State {
        self.initial_value
    }

    // Generates the widgets for this component, based on the current state.
    fn view<'a>(&'a self, state: &'a i32) -> Node<'a, Message> {
        // You can build the view using declarative syntax with the view! macro,
        //  but you can also construct widgets using normal rust code.
        view! {
            Column => {
                Button { text: "Up", on_clicked: Message::UpPressed }
                Text { val: format!("Count: {}", *state) }
                Button { text: "Down", on_clicked: Message::DownPressed }
            }
        }
    }

    // Updates the component state based on a message.
    fn update(&self, message: Message, mut state: State<i32>, _context: Context<Message, ()>) {
        match message {
            Message::UpPressed => *state += 1,
            Message::DownPressed => *state -= 1,
        }
    }
}

#[tokio::main]
async fn main() {
    use winit::window::WindowBuilder;

    Sandbox::new(
        Counter { initial_value: 15 }, 
        StyleBuilder::default(), 
        WindowBuilder::new()
            .with_title("Counter")
            .with_inner_size(winit::dpi::LogicalSize::new(240, 240))
    )
    .await
    .expect("failed to load style")
    .run()
    .await;
}

Examples

If you want more examples, check out the examples directory in the git repository.

Dependencies

~27MB
~325K SLoC