Crates.io | gtk-properties-macro |
lib.rs | gtk-properties-macro |
version | 0.1.0 |
source | src |
created_at | 2022-09-04 16:48:25.501962 |
updated_at | 2022-09-04 16:48:25.501962 |
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()) }