Skip to content

Commit 080fcc0

Browse files
yuraizbilelmoussaoui
authored andcommitted
gtk4-macros: Support blueprint files in CompositeTemplate
1 parent ec6e7b0 commit 080fcc0

File tree

4 files changed

+121
-6
lines changed

4 files changed

+121
-6
lines changed

gtk4-macros/src/composite_template_derive.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,28 @@ use crate::{attribute_parser::*, util::*};
1717

1818
fn gen_set_template(source: &TemplateSource, crate_ident: &proc_macro2::Ident) -> TokenStream {
1919
match source {
20-
TemplateSource::File(file) => quote! {
21-
#crate_ident::subclass::widget::WidgetClassSubclassExt::set_template_static(
22-
klass,
23-
include_bytes!(#file),
24-
);
25-
},
20+
TemplateSource::File(file) => {
21+
let template = if file.ends_with(".blp") {
22+
if cfg!(feature = "blueprint") {
23+
quote! {
24+
#crate_ident::gtk4_macros::include_blueprint!(#file).as_bytes()
25+
}
26+
} else {
27+
panic!("blueprint feature is disabled")
28+
}
29+
} else {
30+
quote! {
31+
include_bytes!(#file)
32+
}
33+
};
34+
35+
quote! {
36+
#crate_ident::subclass::widget::WidgetClassSubclassExt::set_template_static(
37+
klass,
38+
#template,
39+
);
40+
}
41+
}
2642
TemplateSource::Resource(resource) => quote! {
2743
#crate_ident::subclass::widget::WidgetClassSubclassExt::set_template_from_resource(
2844
klass,

gtk4-macros/src/lib.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,49 @@ use proc_macro::TokenStream;
1515
use proc_macro_error::proc_macro_error;
1616
use syn::{parse_macro_input, DeriveInput};
1717

18+
/// That macro includes and compiles blueprint file by path relative to project rood
19+
///
20+
/// It expected to run inside composite_template_derive, not by users
21+
#[cfg(feature = "blueprint")]
22+
#[proc_macro]
23+
pub fn include_blueprint(input: TokenStream) -> TokenStream {
24+
use quote::quote;
25+
26+
let tokens: Vec<_> = input.into_iter().collect();
27+
28+
if tokens.len() != 1 {
29+
panic!("File name not found");
30+
}
31+
32+
let root = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
33+
34+
let file_name = tokens[0].to_string();
35+
let file_name = file_name.trim();
36+
let file_name = &file_name[1..file_name.len() - 1];
37+
38+
let path = std::path::Path::new(&root).join(file_name);
39+
40+
if !path.exists() {
41+
panic!("{path:?} not found");
42+
}
43+
44+
let path = path.to_string_lossy().to_string();
45+
46+
let template = blueprint::compile_blueprint(
47+
std::fs::read_to_string(&path)
48+
.unwrap_or_else(|err| panic!("{err}"))
49+
.as_bytes(),
50+
)
51+
.unwrap();
52+
53+
quote!({
54+
// Compiler reruns macro if file changed
55+
_ = include_str!(#path);
56+
#template
57+
})
58+
.into()
59+
}
60+
1861
/// Derive macro for using a composite template in a widget.
1962
///
2063
/// The `template` attribute specifies where the template should be loaded

gtk4-macros/tests/my_widget.blp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
template MyWidget5 {
2+
Label label {
3+
label: 'foobar';
4+
}
5+
6+
Label my_label2 {
7+
label: 'foobaz';
8+
}
9+
}

gtk4-macros/tests/templates.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,50 @@ glib::wrapper! {
256256
fn blueprint_template() {
257257
let _: MyWidget4 = glib::Object::new();
258258
}
259+
260+
#[cfg(feature = "blueprint")]
261+
mod imp5 {
262+
use super::*;
263+
264+
#[derive(Debug, Default, CompositeTemplate)]
265+
#[template(file = "tests/my_widget.blp")]
266+
pub struct MyWidget5 {
267+
#[template_child]
268+
pub label: TemplateChild<gtk::Label>,
269+
#[template_child(id = "my_label2")]
270+
pub label2: gtk::TemplateChild<gtk::Label>,
271+
}
272+
273+
#[glib::object_subclass]
274+
impl ObjectSubclass for MyWidget5 {
275+
const NAME: &'static str = "MyWidget5";
276+
type Type = super::MyWidget5;
277+
type ParentType = gtk::Widget;
278+
fn class_init(klass: &mut Self::Class) {
279+
klass.bind_template();
280+
}
281+
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
282+
obj.init_template();
283+
}
284+
}
285+
286+
impl ObjectImpl for MyWidget5 {
287+
fn dispose(&self) {
288+
while let Some(child) = self.obj().first_child() {
289+
child.unparent();
290+
}
291+
}
292+
}
293+
impl WidgetImpl for MyWidget5 {}
294+
}
295+
296+
#[cfg(feature = "blueprint")]
297+
glib::wrapper! {
298+
pub struct MyWidget5(ObjectSubclass<imp5::MyWidget5>) @extends gtk::Widget;
299+
}
300+
301+
#[gtk::test]
302+
#[cfg(feature = "blueprint")]
303+
fn blueprint_file() {
304+
let _: MyWidget5 = glib::Object::new();
305+
}

0 commit comments

Comments
 (0)