declarative 0.1.0

Declarative Syntax Macros
Documentation

declarative

Matrix REUSE status

A proc-macro library for creating complex reactive views declaratively and quickly.

To use it, add to your Cargo.toml:

[dependencies.declarative]
version = '0.1'

# for a custom builder mode:
features = ['builder-mode']

# if you're going to use it with gtk-rs, you might want to:
features = ['gtk-rs'] # gives a suitable `builder_mode!` macro

To learn how to use this macro, it is best to clone this repository, read the source code of the examples in alphabetical order and run them like this:

cargo run --features gtk-rs --example EXAMPLE_NAME

The examples depend on gtk-rs, so you should familiarize yourself with gtk-rs a bit before:
https://gtk-rs.org/gtk4-rs/stable/latest/book/

Application example

In the following I manually implement the Elm pattern. The macro does not require any specific pattern.

Light theme app screenshot Dark theme app screenshot

use declarative::{builder_mode, clone};
use gtk::{glib, prelude::*};

#[derive(Debug)]
enum Msg { Increase, Decrease }

struct State { count: i32 }

fn update_state(state: &mut State, msg: Msg) {
	match msg {
		Msg::Increase => state.count = state.count.wrapping_add(1),
		Msg::Decrease => state.count = state.count.wrapping_sub(1),
	}
}

#[declarative::view {
	gtk::ApplicationWindow window !{
		application: app
		title: "My Application"
		
		gtk::HeaderBar #titlebar(&#) { }
		
		gtk::Box #child(&#) !{
			orientation: gtk::Orientation::Vertical
			spacing: 6
			margin_top: 6
			margin_bottom: 6
			margin_start: 6
			margin_end: 6 #..
			
			gtk::Label #append(&#) {
				'bind! set_label: &format!("The count is: {}", state.count)
			}
			
			gtk::Button::with_label("Increase") #append(&#) {
				connect_clicked: clone! {
					sender; move |_| send!(Msg::Increase => sender)
				}
			}
			
			gtk::Button::with_label("Decrease") #append(&#) {
				connect_clicked: move |_| send!(Msg::Decrease => sender)
			}
			
			'binding update_view = move |state: &State| bindings!()
		}
	}
}]

fn window(app: &gtk::Application) -> gtk::ApplicationWindow {
	let mut state = State { count: 0 };
	let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
	
	expand_view_here! { }
	
	receiver.attach(None, move |msg| {
		update_state(&mut state, msg);
		update_view(&state);
		glib::Continue(true)
	});
	
	window
}

fn main() -> glib::ExitCode {
	let app = gtk::Application::default();
	app.connect_activate(move |app| window(app).present());
	app.run()
}

macro_rules! send {
	($expr:expr => $sender:ident) => {
		$sender.send($expr).unwrap_or_else(
			move |error| glib::g_critical!("example", "{error}")
		)
	};
}

use send;

To execute, run:

cargo run --features gtk-rs --example readme

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.