| Crates.io | gtk-properties-macro |
| lib.rs | gtk-properties-macro |
| version | 0.1.0 |
| created_at | 2022-09-04 16:48:25.501962+00 |
| updated_at | 2022-09-04 16:48:25.501962+00 |
| description | Experimental property declaration macro for gtk-rs |
| homepage | |
| repository | https://github.com/nilclass/gtk-properties-macro |
| max_upload_size | |
| id | 658365 |
| size | 75,888 |
⚠ experimental project ⚠
This package contains a macro that makes it easier to declare object properties when using gtk-rs.
For a general introduction to GTK properties in Rust, please refer to the "Properties" chapter of the gtk-rs book.
Add this to your Cargo.toml:
gtk-properties-macro = "0.1"
⚠ requires rust nightly
This is a minimal example, that is functionally equivalent to this one from the book.
For a more complete (working) example, please see the examples/ directory. If you are interested in the code that is generated by the macro, check out the files in tests/expands/.
Here we go:
use gtk_properties_macro::properties;
impl ObjectImpl for CustomButton {
properties! {
#[int]
"number" => {
get {
self.number.get().to_value()
}
set {
let input_number =
value.get().expect("The value needs to be of type `i32`.");
self.number.replace(input_number);
}
}
}
fn constructed(&self, boj: &Self::Type) {
...
}
}
Let's go through it one by one:
properties! { ... }
here we are invoking the properties macro. It must be called within a impl ObjectImpl for ... block, and will implement three methods: properties, property and set_property.
#[int]
"number" => { ... }
this is a property declaration. In this case it declares a property named "number", which has type "int" ("int" here corresponds to ParamSpecInt, more on that later).
get {
self.number.get().to_value()
}
this specifies how to "get" the property's value. The block becomes part of the fn property implementation. Within the 'get' block, we have access to:
self: the "inner" struct of our objectobject: the outer objectid: ID of this property (usize)pspec: ParamSpec of this propertyThe block must evaluate to a glib::Value
set {
let input_number =
value.get().expect("The value needs to be of type `i32`.");
self.number.replace(input_number);
}
corresponding "set" block for the property. It becomes part of the fn set_property implementation. Within the 'set' block we have access to:
value: a glib::Value containing the value being set.Implementing properties, property and set_property manually has some disadvantages:
Within the properties! block, a list of property declarations is expected.
Each declaration consists of:
#[int(minimum = 3, maximum = 27)]#[doc = "..."] (the compiler transforms doc comments into these). These doc comments are all concatenated and stored in the blurb of the param spec."property-name" => { /* implementation block */ }The type declaration is in the form of an attribute. It starts with a "type tag", followed by an (optional) parenthesized list of flags and key/value pairs.
Example:
#[int(construct, nick = "Great Integer", explicit_notify)
int is the type tag, which determines the type of ParamSpec*Builder to use (see table below for supported type tags)explicit_notify and construct are flags, which will be passed to the param spec builder: builder.flags(ParamFlags::CONSTRUCT | ParamFlags::EXPLICIT_NOTIFY)nick = "Great Integer" is a key/value pair, which becomes a method call on the builder: builder.nick("My Number").There is one exception currently to how these arguments are interpreted: if the type tag is object, the first argument must be a gobject type. Example:
#[object(gtk::Button, more, flags, here, ...)]
Since this is an experiment, only a couple of types are supported at this time:
| ParamSpec type | type tag |
|---|---|
| ParamSpecBoolean | boolean |
| ParamSpecBoxed | - |
| ParamSpecChar | char |
| ParamSpecDouble | double |
| ParamSpecEnum | - |
| ParamSpecFlags | - |
| ParamSpecFloat | float |
| ParamSpecGType | - |
| ParamSpecInt | int |
| ParamSpecInt64 | int64 |
| ParamSpecLong | long |
| ParamSpecObject | object(some::glib::Object) |
| ParamSpecOverride | - |
| ParamSpecParam | - |
| ParamSpecPointer | - |
| ParamSpecString | string |
| ParamSpecUChar | - |
| ParamSpecUInt | - |
| ParamSpecUInt64 | - |
| ParamSpecULong | - |
| ParamSpecUnichar | - |
| ParamSpecValueArray | - |
| ParamSpecVariant | - |
A property definition must implement at least one of 'get' or 'set'.
If only one is implemented, ParamFlags::READABLE or ParamFlags::WRITABLE flags are implicitly set correspondingly.
If both are implemented, ParamFlags::READWRITE is implied.
Each block becomes part of the fn property and fn set_property methods respectively.
Example:
impl ObjectImpl for MyObject {
properties! {
#[int]
"my-number" => {
get { self.my_number.get() } // assuming `my_number` is `Cell<Value>` here for simplicity
set { self.my_number.replace(value); }
}
#[string]
"my-string" => {
get { self.my_string.get() }
set { self.my_string.replace(value); }
}
}
}
generates a property function like this:
fn property(&self, object: &Self::Type, id: usize, pspec: ParamSpec) -> Value {
match id {
1 => self.my_number().get(),
2 => self.my_string().get(),
_ => unimplemented!()
}
}
and a corresponding set_property function like this:
fn set_property(&self, object: &Self::Type, id: usize, value: Value, pspec: ParamSpec) {
match id {
1 => {
self.my_number().replace(value);
}
2 => {
self.my_string().replace(value);
}
_ => unimplemented!()
}
}
properties! {
...
// a custom property ('spec' block required), denoted by `_`:
_ => {
spec { ParamSpecSomething::builder("my-custom-prop").build() }
get { ... }
set { ... }
}
...
}
ParamSpecValueArray, by parsing nested declaration as first arg:
properties! {
...
// sth like ParamSpecValueArray::builder("my-string-array", ParamSpecString::builder("my-string").build()).flags(...).build()
#[array(string(name = "my-string"), explicit_notify)]
"my-string-array" => {
...
}
...
}
struct MyObject {
x: Cell<i32>,
y: Cell<i32>,
z: Cell<i32>,
}
impl ObjectImpl for MyObject {
properties! {
#[int] "x" => cell(x),
#[int] "y" => cell(y),
#[int] "z" => cell(z),
}
}
where cell(x) is eqivalent to
get { self.x.get().to_value() }
set { self.x.replace(value.get().unwrap()) }