diff --git a/src/analysis/functions.rs b/src/analysis/functions.rs index 8b201e044..97429de35 100644 --- a/src/analysis/functions.rs +++ b/src/analysis/functions.rs @@ -98,6 +98,8 @@ pub struct Info { /// this potential global function is defined pub ns_id: NsId, pub generate_doc: bool, + pub get_property: Option, + pub set_property: Option, } impl Info { @@ -942,6 +944,8 @@ fn analyze_function( hidden: false, ns_id, generate_doc, + get_property: func.get_property.clone(), + set_property: func.set_property.clone(), } } diff --git a/src/analysis/properties.rs b/src/analysis/properties.rs index 20b1b5719..aed773d5e 100644 --- a/src/analysis/properties.rs +++ b/src/analysis/properties.rs @@ -143,6 +143,19 @@ fn analyze_property( let mut set_func_name = format!("set_{name_for_func}"); let mut set_prop_name = Some(format!("set_property_{name_for_func}")); + let has_getter = prop.getter.as_ref().is_some_and(|getter| { + obj.functions + .matched(getter) + .iter() + .all(|f| f.status.need_generate()) + }); + let has_setter = prop.setter.as_ref().is_some_and(|setter| { + obj.functions + .matched(setter) + .iter() + .all(|f| f.status.need_generate()) + }); + let mut readable = prop.readable; let mut writable = if prop.construct_only { false @@ -219,7 +232,7 @@ fn analyze_property( let (get_out_ref_mode, set_in_ref_mode, nullable) = get_property_ref_modes(env, prop); - let getter = if readable { + let getter = if readable && !has_getter { if let Ok(rust_type) = RustType::builder(env, prop.typ) .direction(library::ParameterDirection::Out) .try_build() @@ -249,7 +262,7 @@ fn analyze_property( None }; - let setter = if writable { + let setter = if writable && !has_setter { if let Ok(rust_type) = RustType::builder(env, prop.typ) .direction(library::ParameterDirection::In) .try_build() diff --git a/src/codegen/function.rs b/src/codegen/function.rs index 03ee417b7..13c0521ae 100644 --- a/src/codegen/function.rs +++ b/src/codegen/function.rs @@ -106,6 +106,16 @@ pub fn generate( if analysis.codegen_name() != analysis.func_name { doc_alias(w, &analysis.func_name, comment_prefix, indent)?; } + if let Some(get_property) = &analysis.get_property { + if get_property != analysis.codegen_name() { + doc_alias(w, get_property, comment_prefix, indent)?; + } + } + if let Some(set_property) = &analysis.set_property { + if set_property != analysis.codegen_name() { + doc_alias(w, set_property, comment_prefix, indent)?; + } + } // Don't add a guard for public or copy/equal functions let dead_code_cfg = if !analysis.visibility.is_public() && !analysis.is_special() { "#[allow(dead_code)]" diff --git a/src/library.rs b/src/library.rs index 1b0f60640..3c3da07d1 100644 --- a/src/library.rs +++ b/src/library.rs @@ -516,6 +516,8 @@ pub struct Property { pub deprecated_version: Option, pub doc: Option, pub doc_deprecated: Option, + pub getter: Option, + pub setter: Option, } #[derive(Clone, Debug)] @@ -551,6 +553,8 @@ pub struct Function { pub deprecated_version: Option, pub doc: Option, pub doc_deprecated: Option, + pub get_property: Option, + pub set_property: Option, } #[derive(Debug)] diff --git a/src/parser.rs b/src/parser.rs index dae9c1704..c1daf3a3f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -595,6 +595,8 @@ impl Library { deprecated_version, doc, doc_deprecated, + get_property: None, + set_property: None, }) } else { Err(parser.fail("Missing element")) @@ -1018,6 +1020,8 @@ impl Library { let is_method = kind == FunctionKind::Method; let version = self.read_version(parser, ns_id, elem)?; let deprecated_version = self.read_deprecated_version(parser, ns_id, elem)?; + let mut gtk_get_property = None; + let mut gtk_set_property = None; let mut params = Vec::new(); let mut ret = None; @@ -1042,9 +1046,34 @@ impl Library { "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), "doc-version" => parser.ignore_element(), "source-position" => parser.ignore_element(), - "attribute" => parser.ignore_element(), + "attribute" => { + if let (Some(name), Some(value)) = (elem.attr("name"), elem.attr("value")) { + match name { + "org.gtk.Method.get_property" => { + gtk_get_property = Some(value.to_string()); + Ok(()) + } + "org.gtk.Method.set_property" => { + gtk_set_property = Some(value.to_string()); + Ok(()) + } + _ => parser.ignore_element(), + } + } else { + parser.ignore_element() + } + } _ => Err(parser.unexpected_element(elem)), })?; + + let get_property = elem + .attr("get-property") + .map(ToString::to_string) + .or(gtk_get_property); + let set_property = elem + .attr("set-property") + .map(ToString::to_string) + .or(gtk_set_property); // The last argument of a callback is ALWAYS user data, so it has to be marked as such // in case it's missing. if is_callback && params.last().map(|x| x.closure.is_none()).unwrap_or(false) { @@ -1082,6 +1111,8 @@ impl Library { deprecated_version, doc, doc_deprecated, + get_property, + set_property, }) } else { Err(parser.fail_with_position( @@ -1325,6 +1356,8 @@ impl Library { let mut typ = None; let mut doc = None; let mut doc_deprecated = None; + let mut gtk_getter = None; + let mut gtk_setter = None; parser.elements(|parser, elem| match elem.name() { "type" | "array" => { @@ -1347,10 +1380,28 @@ impl Library { } "doc" => parser.text().map(|t| doc = Some(t)), "doc-deprecated" => parser.text().map(|t| doc_deprecated = Some(t)), - "attribute" => parser.ignore_element(), + "attribute" => { + if let (Some(name), Some(value)) = (elem.attr("name"), elem.attr("value")) { + match name { + "org.gtk.Property.get" => { + gtk_getter = Some(value.to_string()); + Ok(()) + } + "org.gtk.Property.set" => { + gtk_setter = Some(value.to_string()); + Ok(()) + } + _ => parser.ignore_element(), + } + } else { + parser.ignore_element() + } + } _ => Err(parser.unexpected_element(elem)), })?; + let getter = elem.attr("getter").map(ToString::to_string).or(gtk_getter); + let setter = elem.attr("setter").map(ToString::to_string).or(gtk_setter); if has_empty_type_tag { return Ok(None); } @@ -1369,6 +1420,8 @@ impl Library { deprecated_version, doc, doc_deprecated, + getter, + setter, })) } else { Err(parser.fail_with_position(