Azul


  • Cross-platform GUI toolkit (Windows, Linux, Mac)
  • IMGUI / MVVM-like programming model
  • DOM-based stateless components - Widget drawing done using pure functions
  • CSS-like styling engine, support for many common CSS properties
  • Flexbox-based layout description
  • Built-in standard controls for common user interface elements
  • Custom widgets via function composition, easy dynamic UI composition
  • SVG rendering engine, 2D drawing helpers (lines, circles, rects, etc.)
  • OpenGL integration
  • Async I/O helpers
  • Optional integrated logging and error reporting helpers
  • Single deployment binary, minimal binary size (roughly 5MB all-incl.),
    CPU (0 - 4%) and RAM usage (~ 50MB total)
  • Fast redraw time (0.5 - 4ms), efficient state caching included
Calculator built using the Azul GUI framework Spreadsheet built with Azul GUI framework running on Windows SVG widget displaying a tiger on Linux

extern crate azul; use azul::{prelude::*, widgets::{label::Label, button::Button}}; struct MyDataModel { counter: usize,
} impl Layout for MyDataModel { fn layout(&self, _info: WindowInfo<Self>) -> Dom<Self> { let label = Label::new(format!("{}", self.counter)).dom(); let button = Button::with_label("Update counter").dom() .with_callback(On::MouseUp, Callback(update_counter)); Dom::new(NodeType::Div) .with_child(label) .with_child(button) }
} fn update_counter(app_state: &mut AppState<MyDataModel>, _event: WindowEvent<MyDataModel>) -> UpdateScreen { app_state.data.modify(|state| state.counter += 1); UpdateScreen::Redraw
} fn main() { let app = App::new(MyDataModel { counter: 0 }, AppConfig::default()); app.run(Window::new(WindowCreateOptions::default(), Css::native()).unwrap()).unwrap();
}

Easily compose custom widgets together by appending their DOM trees together. No macros, meta-compiler or external scripting language required. You can store your own widgets in external crates and re-use them throughout your projects.


struct DataModel { emails: Vec<EmailInfo>,
} struct EmailInfo { from: String, subject: String, profile_pic: ImageId, message: TextId,
} impl Layout for DataModel { fn layout(&self, _info: WindowInfo<Self>) -> Dom<Self> { Dom::new(NodeType::Div).with_id("email-container") .with_child(self.emails.iter().map(layout_email_component).collect()) }
} fn layout_email_component(email: &EmailInfo) -> Dom<DataModel> { Dom::new(NodeType::Div) .with_child(Dom::new(NodeType::Div) .with_class("email-header") .with_child(Dom::new(NodeType::Image(email.profile_pic))) .with_child(Dom::new(NodeType::Label(email.from.clone())))) .with_child(Dom::new(NodeType::Div) .with_class("email-content") .with_child(Dom::new(NodeType::Label(email.subject.clone()))) .with_child(Dom::new(NodeType::Text(email.message))))
}

Choose whether you want a platform-native look or a custom, flashy style!

{{ TODO: Screenshots go here }}

Azul provides simple helpers for asynchronous I/O, which are thread-based. Each task is a single thread, polled for completion by azul.


impl Layout for DataModel { fn layout(&self, _info: WindowInfo<Self>) -> Dom<Self> { let button = Button::with_label("Connect to database...").dom() .with_callback(On::MouseUp, Callback(start_connection)); let status = Label::new(match &self.connection_status { Connected => format!("You are connected!"), Err(e) => format!("There was an error: {}", e), InProgress => format!("Loading..."), }).dom(); Dom::new(NodeType::Div) .with_child(status) .with_child(button) }
} fn start_connection(app_state: &mut AppState<DataModel>, _event: WindowEvent<DataModel>) -> UpdateScreen { app_state.data.modify(|state| state.connection_status = ConnectionStatus::InProgress); app_state.add_task(connect_to_db_async, &[]); UpdateScreen::Redraw
} fn connect_to_db_async(app_data: Arc<Mutex<DataModel>>, _: Arc<()>) { thread::sleep(Duration::from_secs(2)); // simulate slow load app_data.modify(|state| state.connection_status = ConnectionStatus::Connected);
}

Contrary to other IMGUI-like toolkits, azul provides automatic two way data binding - only minimal code changes to go from a static label to a dynamic input form.


struct DataModel { text_input_state: TextInputState,
} impl Layout for DataModel { fn layout(&self, info: WindowInfo<Self>) -> Dom<Self> { // Create a new text input field TextInput::new() // ... bind it to self.text_input - will automatically update .bind(info.window, &self.text_input_state, &self) // ... and render it in the UI .dom(&self.text_input_state) .with_callback(On::KeyUp, Callback(print_text_field)) }
} fn print_text_field(app_state: &mut AppState<DataModel>, _event: WindowEvent<DataModel>) -> UpdateScreen { println!("You've typed: {}", app_state.data.lock().unwrap().text_input_state.text); UpdateScreen::DontRedraw
}

Interested? Check out our user guide!