Skip to content

Commit 1d1af2f

Browse files
Add support for new QProperty flags (Reset, Constant, Required, Final)
1 parent 0ed19c6 commit 1d1af2f

File tree

12 files changed

+374
-62
lines changed

12 files changed

+374
-62
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
- Serde support for `QString` (requires "serde" feature on cxx-qt-lib)
2828
- A new QuickControls module, which exposes `QQuickStyle`. This module is enabled by default and is behind the `qt_quickcontrols` feature.
2929
- Add support for specifying read write and notify in qproperty macro, including support for custom user defined functions
30+
- Add support for the constant, required, reset and final flags in the qproperty macro
3031

3132
### Changed
3233

book/src/bridge/extern_rustqt.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,22 +132,41 @@ Where `<Property>` is the name of the property.
132132
These setters and getters assure that the changed signal is emitted every time the property is edited.
133133

134134
It is also possible to specify custom getters, setters and notify signals, using flags passed like so:
135-
`#[qproperty(TYPE, NAME, read = myGetter, write = mySetter, notify = myOnChanged)]`
135+
`#[qproperty(TYPE, NAME, READ = myGetter, WRITE = mySetter, NOTIFY = myOnChanged)]`
136+
> Note: the key for the flags use all capitals like in the Qt version of qproperty
136137
137138
> Note: currently the rust name must be in camel case or specified like `#[cxx_name = "my_getter"]` if not
138139
139140
It is also possible to use any combination of custom functions or omit them entirely, but if flags are specified, read must be included as all properties need to be able to be read.
140141

141-
Using the read flag will cause CXX-Qt to generate a getter function with an automatic name based off the property. e.g. `#[qproperty(i32, num, read)]` will have a getter function generated called get_num in Rust, and getNum in C++.
142+
Using the read flag will cause CXX-Qt to generate a getter function with an automatic name based off the property. e.g. `#[qproperty(i32, num, READ)]` will have a getter function generated called get_num in Rust, and getNum in C++.
142143

143144
If a custom function is specified, an implementation both in qobject::MyObject and and export in the bridge is expected.
144145

145146
### Examples
146147

147-
- `#[qproperty(TYPE, NAME, read)]` A read only prop
148-
- `#[qproperty(TYPE, NAME, read = myGetter, write, notify)]` custom getter provided but will generate setter and on-changed
149-
- `#[qproperty(TYPE, NAME)]` is syntactic sugar for `#[qproperty(TYPE, NAME, read, write, notify)]`
150-
- `#[qproperty(TYPE, NAME, write)]` is an error as read was not explicitly passed
148+
- `#[qproperty(TYPE, NAME, READ)]` A read only property
149+
- `#[qproperty(TYPE, NAME, READ = myGetter, WRITE, NOTIFY)]` custom getter provided, but will generate setter and on-changed
150+
- `#[qproperty(TYPE, NAME)]` is shorthand for `#[qproperty(TYPE, NAME, READ, WRITE, NOTIFY)]`
151+
- `#[qproperty(TYPE, NAME, WRITE)]` is an error as read was not explicitly passed
152+
153+
### Available Flags
154+
155+
- `READ` or `READ = my_getter`
156+
- Specifies that the property should be readable (*always required if flags are passed*), with optional user defined getter
157+
- `WRITE` or `WRITE = my_setter`
158+
- Specifies that the property should be writeable, with optional user defined setter
159+
- `NOTIFY` or `NOTIFY = my_on_changed`
160+
- Specifies that the property should emit a notify signal on change, with optional user defined signal name
161+
- `CONSTANT`
162+
- Specifies that the property should be constant (implication is that the getter returns the same value every time for that particular instance)
163+
- __`CONSTANT` is not available for properties which use `WRITE` or `NOTIFY` and will not compile__
164+
- `REQUIRED`
165+
- Specifies that the property must be set by a user of the class, useful in QML as the class cannot be instantiated unless the property has been set
166+
- `FINAL`
167+
- Specifies that the property will not be overriden by a derived class
168+
- `RESET = my_reset`
169+
- Specifies a function to reset the property to a default value, user function __must__ be provided or it will not compile
151170

152171
## Methods
153172

book/src/getting-started/2-our-first-cxx-qt-module.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ Also, we can't move the `QObject`, which is why it is behind a Rust [`Pin`](http
176176

177177
CXX-Qt will generate getters and setters for all properties of our `struct`.
178178
That's where the `number()` and `set_number()` functions used by `increment_number()` come from.
179+
See the [`QProperty` section](../bridge/extern_rustqt.md#properties) for details on user defined properties.
180+
179181
For more details on what you can do with the `QObject` from Rust and what functions CXX-Qt will generate for you, take a look at the [`QObject` page](../concepts/generated_qobject.md).
180182

181183
And that's it. We've defined our first `QObject` subclass in Rust. That wasn't so hard, was it?

crates/cxx-qt-gen/src/generator/cpp/property/meta.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
// SPDX-License-Identifier: MIT OR Apache-2.0
55

66
use crate::generator::naming::property::QPropertyNames;
7+
use crate::parser::property::QPropertyFlags;
78

89
/// Generate the metaobject line for a given property
9-
pub fn generate(idents: &QPropertyNames, cxx_ty: &str) -> String {
10+
pub fn generate(idents: &QPropertyNames, flags: &QPropertyFlags, cxx_ty: &str) -> String {
1011
let mut parts = vec![format!(
1112
"READ {ident_getter}",
1213
ident_getter = idents.getter.cxx_unqualified()
@@ -20,6 +21,22 @@ pub fn generate(idents: &QPropertyNames, cxx_ty: &str) -> String {
2021
parts.push(format!("NOTIFY {}", notify.cxx_unqualified()));
2122
}
2223

24+
if let Some(reset) = &idents.reset {
25+
parts.push(format!("RESET {}", reset.cxx_unqualified()));
26+
}
27+
28+
if flags.constant {
29+
parts.push(String::from("CONSTANT"))
30+
}
31+
32+
if flags.required {
33+
parts.push(String::from("REQUIRED"))
34+
}
35+
36+
if flags.is_final {
37+
parts.push(String::from("FINAL"))
38+
}
39+
2340
format!(
2441
"Q_PROPERTY({ty} {ident} {meta_parts})",
2542
ty = cxx_ty,

crates/cxx-qt-gen/src/generator/cpp/property/mod.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ pub fn generate_cpp_properties(
3131
let idents = QPropertyNames::from(property);
3232
let cxx_ty = syn_type_to_cpp_type(&property.ty, type_names)?;
3333

34-
generated.metaobjects.push(meta::generate(&idents, &cxx_ty));
34+
generated
35+
.metaobjects
36+
.push(meta::generate(&idents, &property.flags, &cxx_ty));
3537

3638
if let Some(getter) = getter::generate(&idents, &qobject_ident, &cxx_ty) {
3739
generated.methods.push(getter);
@@ -76,20 +78,25 @@ mod tests {
7678
use quote::format_ident;
7779
use syn::{parse_quote, ItemStruct};
7880

79-
#[test]
80-
fn test_custom_setter() {
81-
let mut input: ItemStruct = parse_quote! {
82-
#[qproperty(i32, num, read, write = mySetter)]
83-
struct MyStruct;
84-
};
81+
fn setup_generated(input: &mut ItemStruct) -> Result<GeneratedCppQObjectBlocks> {
8582
let property = ParsedQProperty::parse(input.attrs.remove(0)).unwrap();
8683

8784
let properties = vec![property];
8885

8986
let qobject_idents = create_qobjectname();
9087

9188
let type_names = TypeNames::mock();
92-
let generated = generate_cpp_properties(&properties, &qobject_idents, &type_names).unwrap();
89+
generate_cpp_properties(&properties, &qobject_idents, &type_names)
90+
}
91+
92+
#[test]
93+
fn test_custom_setter() {
94+
let mut input: ItemStruct = parse_quote! {
95+
#[qproperty(i32, num, READ, WRITE = mySetter)]
96+
struct MyStruct;
97+
};
98+
99+
let generated = setup_generated(&mut input).unwrap();
93100

94101
assert_eq!(generated.metaobjects.len(), 1);
95102
assert_str_eq!(
@@ -119,10 +126,39 @@ mod tests {
119126
);
120127
}
121128

129+
#[test]
130+
fn test_reset() {
131+
let mut input: ItemStruct = parse_quote! {
132+
#[qproperty(i32, num, READ, WRITE = mySetter, RESET = my_resetter)]
133+
struct MyStruct;
134+
};
135+
136+
let generated = setup_generated(&mut input).unwrap();
137+
138+
assert_str_eq!(
139+
generated.metaobjects[0],
140+
"Q_PROPERTY(::std::int32_t num READ getNum WRITE mySetter RESET my_resetter)"
141+
);
142+
}
143+
144+
#[test]
145+
fn test_constant_and_required() {
146+
let mut input: ItemStruct = parse_quote! {
147+
#[qproperty(i32, num, READ, CONSTANT, REQUIRED)]
148+
struct MyStruct;
149+
};
150+
let generated = setup_generated(&mut input).unwrap();
151+
152+
assert_str_eq!(
153+
generated.metaobjects[0],
154+
"Q_PROPERTY(::std::int32_t num READ getNum CONSTANT REQUIRED)"
155+
);
156+
}
157+
122158
#[test]
123159
fn test_generate_cpp_properties() {
124160
let mut input1: ItemStruct = parse_quote! {
125-
#[qproperty(i32, trivial_property, read, write, notify)]
161+
#[qproperty(i32, trivial_property, READ, WRITE, NOTIFY)]
126162
struct MyStruct;
127163
};
128164

crates/cxx-qt-gen/src/generator/naming/property.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ pub struct QPropertyNames {
4646
pub setter: Option<NameState>,
4747
pub setter_wrapper: Option<Name>,
4848
pub notify: Option<NameState>,
49+
pub reset: Option<Name>,
4950
}
5051

5152
impl From<&ParsedQProperty> for QPropertyNames {
@@ -79,12 +80,15 @@ impl From<&ParsedQProperty> for QPropertyNames {
7980
None
8081
};
8182

83+
let reset = flags.reset.as_ref().map(|ident| Name::new(ident.clone()));
84+
8285
Self {
8386
getter_wrapper,
8487
getter,
8588
setter_wrapper,
8689
setter,
8790
notify,
91+
reset,
8892
name: property_name,
8993
}
9094
}

crates/cxx-qt-gen/src/naming/type_names.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,6 @@ mod tests {
574574

575575
#[test]
576576
fn test_cxx_items_cxx_name() {
577-
// TODO
578577
let item: Item = parse_quote! {
579578
unsafe extern "C++" {
580579
#[cxx_name = "B"]

0 commit comments

Comments
 (0)