Skip to content

Commit 88cbc42

Browse files
authored
Merge pull request #1183 from Swivelgames/feature/export-storage
Added @export_storage Export Type
2 parents af32d29 + 3177514 commit 88cbc42

File tree

6 files changed

+69
-16
lines changed

6 files changed

+69
-16
lines changed

godot-core/src/registry/property.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ pub mod export_info_functions {
517517
// right side are the corresponding property hint. Godot is not always consistent between the two, such
518518
// as `export_multiline` being `PROPERTY_HINT_MULTILINE_TEXT`.
519519
default_export_funcs!(
520+
export_storage => NONE, // Storage exports don't display in the editor.
520521
export_flags_2d_physics => LAYERS_2D_PHYSICS,
521522
export_flags_2d_render => LAYERS_2D_RENDER,
522523
export_flags_2d_navigation => LAYERS_2D_NAVIGATION,

godot-macros/src/class/data_models/field_export.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use proc_macro2::{Ident, Span, TokenStream};
99
use quote::quote;
1010
use std::collections::{HashMap, HashSet};
1111

12-
use crate::util::{KvParser, ListParser};
12+
use crate::util::{ident, KvParser, ListParser};
1313
use crate::ParseResult;
1414

1515
pub struct FieldExport {
@@ -27,6 +27,10 @@ impl FieldExport {
2727
pub fn to_export_hint(&self) -> Option<TokenStream> {
2828
self.export_type.to_export_hint()
2929
}
30+
31+
pub fn to_export_usage(&self) -> Option<Ident> {
32+
self.export_type.to_export_usage()
33+
}
3034
}
3135

3236
/// Store info from `#[export]` attribute.
@@ -40,6 +44,20 @@ pub enum ExportType {
4044
/// Can become other property hints, depends on context.
4145
Default,
4246

47+
/// ### GDScript annotations
48+
/// - `@export_storage`
49+
///
50+
/// ### Property hints
51+
/// - `NONE`
52+
///
53+
/// ### Property usage
54+
/// - `STORAGE`
55+
///
56+
/// This is used to indicate that the property should be exported
57+
/// but should not be visible in the editor. Therefore, it does not
58+
/// have a property hint, but uses the `STORAGE` property usage.
59+
Storage,
60+
4361
/// ### GDScript annotations
4462
/// - `@export_range`
4563
///
@@ -150,6 +168,10 @@ impl ExportType {
150168
/// becomes
151169
/// `#[export(flags/enum = (elem1, elem2 = key2, ...))]`
152170
pub(crate) fn new_from_kv(parser: &mut KvParser) -> ParseResult<Self> {
171+
if parser.handle_alone("storage")? {
172+
return Self::new_storage();
173+
}
174+
153175
if let Some(list_parser) = parser.handle_list("range")? {
154176
return Self::new_range_list(list_parser);
155177
}
@@ -273,6 +295,10 @@ impl ExportType {
273295
Ok(Self::Default)
274296
}
275297

298+
fn new_storage() -> ParseResult<Self> {
299+
Ok(Self::Storage)
300+
}
301+
276302
fn new_range_list(mut parser: ListParser) -> ParseResult<Self> {
277303
const FLAG_OPTIONS: [&str; 7] = [
278304
"or_greater",
@@ -404,6 +430,8 @@ impl ExportType {
404430
match self {
405431
Self::Default => None,
406432

433+
Self::Storage => quote_export_func! { export_storage() },
434+
407435
Self::Range {
408436
min,
409437
max,
@@ -519,6 +547,13 @@ impl ExportType {
519547
Self::ColorNoAlpha => quote_export_func! { export_color_no_alpha() },
520548
}
521549
}
550+
551+
pub fn to_export_usage(&self) -> Option<Ident> {
552+
match self {
553+
Self::Storage => Some(ident("STORAGE")),
554+
_ => None,
555+
}
556+
}
522557
}
523558

524559
/// The dimension of a `@export_flags_{dimension}_{layer}` annotation.

godot-macros/src/class/data_models/property.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,17 @@ pub fn make_property_impl(class_name: &Ident, fields: &Fields) -> TokenStream {
5353

5454
// Ensure we add a var if the user only provided a `#[export]`.
5555
let var = match (export, var) {
56-
(Some(_), None) => Some(FieldVar {
57-
usage_flags: UsageFlags::InferredExport,
58-
..Default::default()
59-
}),
56+
(Some(export), None) => {
57+
let usage_flags = if let Some(usage) = export.to_export_usage() {
58+
UsageFlags::Custom(vec![usage])
59+
} else {
60+
UsageFlags::InferredExport
61+
};
62+
Some(FieldVar {
63+
usage_flags,
64+
..Default::default()
65+
})
66+
}
6067

6168
(_, var) => var.clone(),
6269
};

godot-macros/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,11 @@ use crate::util::{bail, ident, KvParser};
243243
/// // @export
244244
/// #[export]
245245
/// float: f64,
246-
///
246+
///
247+
/// // @export_storage
248+
/// #[export(storage)]
249+
/// hidden_string: GString,
250+
///
247251
/// // @export_range(0.0, 10.0, or_greater)
248252
/// #[export(range = (0.0, 10.0, or_greater))]
249253
/// range_f64: f64,

itest/rust/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,9 @@ fn generate_property_template(inputs: &[Input]) -> PropertyTests {
472472
TokenStream::new()
473473
} else {
474474
quote! {
475+
#[export(storage)]
476+
export_storage: GString,
477+
475478
#[export(file)]
476479
export_file_array: Array<GString>,
477480
#[export(file)]
@@ -597,6 +600,7 @@ fn generate_property_template(inputs: &[Input]) -> PropertyTests {
597600

598601
// Only available in Godot 4.3+.
599602
let advanced_exports_4_3 = r#"
603+
@export_storage var export_storage: String
600604
@export_file var export_file_array: Array[String]
601605
@export_file var export_file_parray: PackedStringArray
602606
@export_file("*.txt") var export_file_wildcard_array: Array[String]

itest/rust/src/object_tests/property_template_test.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,15 @@ fn property_template_test(ctx: &TestContext) {
7070

7171
let mut rust_usage = rust_prop.at("usage").to::<i64>();
7272

73-
// the GDSscript variables are script variables, and so have `PROPERTY_USAGE_SCRIPT_VARIABLE` set.
74-
if rust_usage == PropertyUsageFlags::STORAGE.ord() as i64 {
75-
// `PROPERTY_USAGE_SCRIPT_VARIABLE` does the same thing as `PROPERTY_USAGE_STORAGE` and so
76-
// GDScript doesn't set both if it doesn't need to.
77-
rust_usage = PropertyUsageFlags::SCRIPT_VARIABLE.ord() as i64
73+
// The GDSscript variables are script variables, and so have `PROPERTY_USAGE_SCRIPT_VARIABLE` set.
74+
// Before 4.3, `PROPERTY_USAGE_SCRIPT_VARIABLE` did the same thing as `PROPERTY_USAGE_STORAGE` and
75+
// so GDScript didn't set both if it didn't need to.
76+
if GdextBuild::before_api("4.3") {
77+
if rust_usage == PropertyUsageFlags::STORAGE.ord() as i64 {
78+
rust_usage = PropertyUsageFlags::SCRIPT_VARIABLE.ord() as i64
79+
} else {
80+
rust_usage |= PropertyUsageFlags::SCRIPT_VARIABLE.ord() as i64;
81+
}
7882
} else {
7983
rust_usage |= PropertyUsageFlags::SCRIPT_VARIABLE.ord() as i64;
8084
}
@@ -102,11 +106,11 @@ fn property_template_test(ctx: &TestContext) {
102106
errors.push(format!(
103107
"mismatch in property {name}:\n GDScript: {gdscript_prop:?}\n Rust: {rust_prop:?}"
104108
));
105-
} /*else {
106-
println!("matching property {name}\n GDScript: {gdscript_prop:?}\n Rust: {rust_prop:?}");
107-
}*/
109+
}
108110
}
109111

112+
rust_properties.free();
113+
110114
assert!(
111115
properties.is_empty(),
112116
"not all properties were matched, missing: {properties:?}"
@@ -118,6 +122,4 @@ fn property_template_test(ctx: &TestContext) {
118122
errors.len(),
119123
errors.join("\n")
120124
);
121-
122-
rust_properties.free();
123125
}

0 commit comments

Comments
 (0)