by
extern crate azul;
use azul::{prelude::*, widgets::{label::Label, button::Button}};
struct DataModel {
counter: usize,
}
impl Layout for DataModel {
fn layout(&self, _info: LayoutInfo<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::div()
.with_child(label)
.with_child(button)
}
}
fn update_counter(app_state: &mut AppState<DataModel>, _: &mut CallbackInfo<DataModel>) -> UpdateScreen {
app_state.data.modify(|state| state.counter += 1)?;
Redraw
}
fn main() {
let app = App::new(DataModel { counter: 0 }, AppConfig::default()).unwrap();
let window = app.create_window(WindowCreateOptions::default(), css::native()).unwrap();
app.run(window).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: LayoutInfo<Self>) -> Dom<Self> {
Dom::div().with_id("email-container")
.with_child(self.emails.iter().map(layout_email_component).collect())
}
}
fn layout_email_component(email: &EmailInfo) -> Dom<DataModel> {
Dom::div()
.with_child(
Dom::div().with_class("email-header")
.with_child(Dom::image(email.profile_pic))
.with_child(Dom::label(email.from.clone())
).with_child(
Dom::div().with_class("email-content")
.with_child(Dom::label(email.subject.clone())
)
.with_child(Dom::text(email.message))
}
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: LayoutInfo<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::div()
.with_child(status)
.with_child(button)
}
}
fn start_connection(app_state: &mut AppState<DataModel>, _: &mut CallbackInfo<DataModel>) -> UpdateScreen {
app_state.data.modify(|state| state.connection_status = ConnectionStatus::InProgress)?;
app_state.add_task(Task::new(connect_to_db_async));
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).unwrap();
}
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: LayoutInfo<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>, _: &mut CallbackInfo<DataModel>) -> UpdateScreen {
println!("You've typed: {}", app_state.data.ok()?.text_input_state.text);
DontRedraw
}
Interested? Check out our user guide!