Skip to content

Commit b21bfa5

Browse files
authored
improve view property ui methods (#8305)
### Related * split out of #8234 ### What * use `ViewProperty` util construct some more * route individual property ui via `view_property_component_ui_custom` making it easier to draw custom ui for specific view properties while still having all tooltip & extra menus be the same
1 parent 97e498d commit b21bfa5

File tree

3 files changed

+114
-125
lines changed

3 files changed

+114
-125
lines changed

crates/viewer/re_space_view/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ pub use query::{
3030
pub use results_ext::{
3131
HybridLatestAtResults, HybridResults, HybridResultsChunkIter, RangeResultsExt,
3232
};
33-
pub use view_property_ui::view_property_ui;
33+
pub use view_property_ui::{
34+
view_property_component_ui, view_property_component_ui_custom, view_property_ui,
35+
};
3436

3537
pub mod external {
3638
pub use re_entity_db::external::*;
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
use re_chunk_store::{external::re_chunk::Arrow2Array, RowId};
21
use re_types_core::{
3-
reflection::{ArchetypeFieldReflection, ArchetypeReflection},
4-
Archetype, ArchetypeName, ArchetypeReflectionMarker, ComponentName,
2+
reflection::ArchetypeFieldReflection, Archetype, ArchetypeReflectionMarker, ComponentName,
53
};
64
use re_ui::{list_item, UiExt as _};
75
use re_viewer_context::{
86
ComponentFallbackProvider, ComponentUiTypes, QueryContext, SpaceViewId, SpaceViewState,
97
ViewerContext,
108
};
11-
use re_viewport_blueprint::entity_path_for_view_property;
9+
use re_viewport_blueprint::ViewProperty;
1210

1311
/// Display the UI for editing all components of a blueprint archetype.
1412
///
@@ -20,79 +18,49 @@ pub fn view_property_ui<A: Archetype + ArchetypeReflectionMarker>(
2018
fallback_provider: &dyn ComponentFallbackProvider,
2119
view_state: &dyn SpaceViewState,
2220
) {
23-
let name = A::name();
24-
if let Some(reflection) = ctx.reflection.archetypes.get(&name) {
25-
view_property_ui_impl(
26-
ctx,
27-
ui,
28-
view_id,
29-
name,
30-
reflection,
31-
view_state,
32-
fallback_provider,
33-
);
34-
} else {
35-
// The `ArchetypeReflectionMarker` bound should make this impossible.
36-
re_log::warn_once!("Missing reflection data for archetype {name:?}.");
37-
}
21+
let view_property =
22+
ViewProperty::from_archetype::<A>(ctx.blueprint_db(), ctx.blueprint_query, view_id);
23+
view_property_ui_impl(ctx, ui, &view_property, fallback_provider, view_state);
3824
}
3925

4026
fn view_property_ui_impl(
4127
ctx: &ViewerContext<'_>,
4228
ui: &mut egui::Ui,
43-
view_id: SpaceViewId,
44-
name: ArchetypeName,
45-
reflection: &ArchetypeReflection,
46-
view_state: &dyn SpaceViewState,
29+
property: &ViewProperty,
4730
fallback_provider: &dyn ComponentFallbackProvider,
31+
view_state: &dyn SpaceViewState,
4832
) {
49-
let blueprint_path = entity_path_for_view_property(view_id, ctx.blueprint_db().tree(), name);
50-
let query_ctx = QueryContext {
51-
viewer_ctx: ctx,
52-
target_entity_path: &blueprint_path,
53-
archetype_name: Some(name),
54-
query: ctx.blueprint_query,
55-
view_state,
56-
view_ctx: None,
33+
let Some(reflection) = ctx.reflection.archetypes.get(&property.archetype_name) else {
34+
// The `ArchetypeReflectionMarker` bound should make this impossible.
35+
re_log::warn_once!(
36+
"Missing reflection data for archetype {:?}.",
37+
property.archetype_name
38+
);
39+
return;
5740
};
5841

59-
let component_results = ctx.blueprint_db().latest_at(
60-
ctx.blueprint_query,
61-
&blueprint_path,
62-
reflection.fields.iter().map(|field| field.component_name),
63-
);
64-
42+
let query_ctx = property.query_context(ctx, view_state);
6543
// If the property archetype only has a single component, don't show an additional hierarchy level!
6644
if reflection.fields.len() == 1 {
6745
let field = &reflection.fields[0];
6846

69-
let component_array = component_results.component_batch_raw(&field.component_name);
7047
view_property_component_ui(
7148
&query_ctx,
7249
ui,
73-
field.component_name,
50+
property,
7451
reflection.display_name,
7552
field,
76-
&blueprint_path,
77-
component_results.component_row_id(&field.component_name),
78-
component_array.as_deref(),
7953
fallback_provider,
8054
);
8155
} else {
8256
let sub_prop_ui = |ui: &mut egui::Ui| {
8357
for field in &reflection.fields {
84-
let display_name = &field.display_name;
85-
86-
let component_array = component_results.component_batch_raw(&field.component_name);
8758
view_property_component_ui(
8859
&query_ctx,
8960
ui,
90-
field.component_name,
91-
display_name,
61+
property,
62+
field.display_name,
9263
field,
93-
&blueprint_path,
94-
component_results.component_row_id(&field.component_name),
95-
component_array.as_deref(),
9664
fallback_provider,
9765
);
9866
}
@@ -102,7 +70,7 @@ fn view_property_ui_impl(
10270
.interactive(false)
10371
.show_hierarchical_with_children(
10472
ui,
105-
ui.make_persistent_id(name.full_name()),
73+
ui.make_persistent_id(property.archetype_name.full_name()),
10674
true,
10775
list_item::LabelContent::new(reflection.display_name),
10876
sub_prop_ui,
@@ -111,36 +79,91 @@ fn view_property_ui_impl(
11179
}
11280

11381
/// Draw view property ui for a single component of a view property archetype.
114-
#[allow(clippy::too_many_arguments)]
115-
fn view_property_component_ui(
82+
///
83+
/// Use [`view_property_ui`] whenever possible to show the ui for all components of a view property archetype.
84+
/// This function is only useful if you want to show custom ui for some of the components.
85+
pub fn view_property_component_ui(
11686
ctx: &QueryContext<'_>,
11787
ui: &mut egui::Ui,
118-
component_name: ComponentName,
119-
root_item_display_name: &str,
88+
property: &ViewProperty,
89+
display_name: &str,
12090
field: &ArchetypeFieldReflection,
121-
blueprint_path: &re_log_types::EntityPath,
122-
row_id: Option<RowId>,
123-
component_array: Option<&dyn Arrow2Array>,
12491
fallback_provider: &dyn ComponentFallbackProvider,
12592
) {
126-
let singleline_list_item_content = singleline_list_item_content(
127-
ctx,
128-
root_item_display_name,
129-
blueprint_path,
130-
component_name,
131-
row_id,
132-
component_array,
133-
fallback_provider,
134-
);
93+
let component_array = property.component_raw(field.component_name);
94+
let row_id = property.component_row_id(field.component_name);
13595

13696
let ui_types = ctx
13797
.viewer_ctx
13898
.component_ui_registry
139-
.registered_ui_types(component_name);
99+
.registered_ui_types(field.component_name);
100+
101+
let singleline_ui: &dyn Fn(&mut egui::Ui) = &|ui| {
102+
ctx.viewer_ctx.component_ui_registry.singleline_edit_ui(
103+
ctx,
104+
ui,
105+
ctx.viewer_ctx.blueprint_db(),
106+
ctx.target_entity_path,
107+
field.component_name,
108+
row_id,
109+
component_array.as_deref(),
110+
fallback_provider,
111+
);
112+
};
113+
114+
let multiline_ui: &dyn Fn(&mut egui::Ui) = &|ui| {
115+
ctx.viewer_ctx.component_ui_registry.multiline_edit_ui(
116+
ctx,
117+
ui,
118+
ctx.viewer_ctx.blueprint_db(),
119+
ctx.target_entity_path,
120+
field.component_name,
121+
row_id,
122+
component_array.as_deref(),
123+
fallback_provider,
124+
);
125+
};
126+
// Do this as a separate step to avoid borrowing issues.
127+
let multiline_ui_ref: Option<&dyn Fn(&mut egui::Ui)> =
128+
if ui_types.contains(ComponentUiTypes::MultiLineEditor) {
129+
Some(multiline_ui)
130+
} else {
131+
None
132+
};
140133

141-
let list_item_response = if ui_types.contains(ComponentUiTypes::MultiLineEditor) {
134+
view_property_component_ui_custom(
135+
ctx,
136+
ui,
137+
property,
138+
display_name,
139+
field,
140+
singleline_ui,
141+
multiline_ui_ref,
142+
);
143+
}
144+
145+
/// Draw view property ui for a single component of a view property archetype with custom ui for singleline & multiline.
146+
///
147+
/// Use [`view_property_ui`] whenever possible to show the ui for all components of a view property archetype.
148+
/// This function is only useful if you want to show custom ui for some of the components.
149+
pub fn view_property_component_ui_custom(
150+
ctx: &QueryContext<'_>,
151+
ui: &mut egui::Ui,
152+
property: &ViewProperty,
153+
display_name: &str,
154+
field: &ArchetypeFieldReflection,
155+
singleline_ui: &dyn Fn(&mut egui::Ui),
156+
multiline_ui: Option<&dyn Fn(&mut egui::Ui)>,
157+
) {
158+
let singleline_list_item_content = list_item::PropertyContent::new(display_name)
159+
.menu_button(&re_ui::icons::MORE, |ui| {
160+
menu_more(ctx.viewer_ctx, ui, property, field.component_name);
161+
})
162+
.value_fn(move |ui, _| singleline_ui(ui));
163+
164+
let list_item_response = if let Some(multiline_ui) = multiline_ui {
142165
let default_open = false;
143-
let id = egui::Id::new((blueprint_path.hash(), component_name));
166+
let id = egui::Id::new((ctx.target_entity_path.hash(), field.component_name));
144167
ui.list_item()
145168
.interactive(false)
146169
.show_hierarchical_with_children(
@@ -149,16 +172,7 @@ fn view_property_component_ui(
149172
default_open,
150173
singleline_list_item_content,
151174
|ui| {
152-
ctx.viewer_ctx.component_ui_registry.multiline_edit_ui(
153-
ctx,
154-
ui,
155-
ctx.viewer_ctx.blueprint_db(),
156-
blueprint_path,
157-
component_name,
158-
row_id,
159-
component_array,
160-
fallback_provider,
161-
);
175+
multiline_ui(ui);
162176
},
163177
)
164178
.item_response
@@ -177,14 +191,13 @@ fn view_property_component_ui(
177191
fn menu_more(
178192
ctx: &ViewerContext<'_>,
179193
ui: &mut egui::Ui,
180-
blueprint_path: &re_log_types::EntityPath,
194+
property: &ViewProperty,
181195
component_name: ComponentName,
182-
component_array: Option<&dyn Arrow2Array>,
183196
) {
197+
let component_array = property.component_raw(component_name);
198+
184199
let property_differs_from_default = component_array
185-
!= ctx
186-
.raw_latest_at_in_default_blueprint(blueprint_path, component_name)
187-
.as_deref();
200+
!= ctx.raw_latest_at_in_default_blueprint(&property.blueprint_store_path, component_name);
188201

189202
let response = ui
190203
.add_enabled(
@@ -199,7 +212,7 @@ If no default blueprint was set or it didn't set any value for this field, this
199212
"The property is already set to the same value it has in the default blueprint",
200213
);
201214
if response.clicked() {
202-
ctx.reset_blueprint_component_by_name(blueprint_path, component_name);
215+
ctx.reset_blueprint_component_by_name(&property.blueprint_store_path, component_name);
203216
ui.close_menu();
204217
}
205218

@@ -214,43 +227,10 @@ This has the same effect as not setting the value in the blueprint at all."
214227
)
215228
.on_disabled_hover_text("The property is already unset.");
216229
if response.clicked() {
217-
ctx.clear_blueprint_component_by_name(blueprint_path, component_name);
230+
ctx.clear_blueprint_component_by_name(&property.blueprint_store_path, component_name);
218231
ui.close_menu();
219232
}
220233

221234
// TODO(andreas): The next logical thing here is now to save it to the default blueprint!
222235
// This should be fairly straight forward except that we need to make sure that a default blueprint exists in the first place.
223236
}
224-
225-
fn singleline_list_item_content<'a>(
226-
ctx: &'a QueryContext<'_>,
227-
display_name: &str,
228-
blueprint_path: &'a re_log_types::EntityPath,
229-
component_name: ComponentName,
230-
row_id: Option<RowId>,
231-
component_array: Option<&'a dyn Arrow2Array>,
232-
fallback_provider: &'a dyn ComponentFallbackProvider,
233-
) -> list_item::PropertyContent<'a> {
234-
list_item::PropertyContent::new(display_name)
235-
.menu_button(&re_ui::icons::MORE, move |ui| {
236-
menu_more(
237-
ctx.viewer_ctx,
238-
ui,
239-
blueprint_path,
240-
component_name,
241-
component_array,
242-
);
243-
})
244-
.value_fn(move |ui, _| {
245-
ctx.viewer_ctx.component_ui_registry.singleline_edit_ui(
246-
ctx,
247-
ui,
248-
ctx.viewer_ctx.blueprint_db(),
249-
blueprint_path,
250-
component_name,
251-
row_id,
252-
component_array,
253-
fallback_provider,
254-
);
255-
})
256-
}

crates/viewer/re_viewport_blueprint/src/view_properties.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,17 @@ pub struct ViewProperty {
3333
/// stored.
3434
pub blueprint_store_path: EntityPath,
3535

36-
archetype_name: ArchetypeName,
37-
component_names: Vec<ComponentName>,
38-
query_results: LatestAtResults,
39-
blueprint_query: LatestAtQuery,
36+
/// Name of the property archetype.
37+
pub archetype_name: ArchetypeName,
38+
39+
/// List of all components in this property.
40+
pub component_names: Vec<ComponentName>,
41+
42+
/// Query results for all queries of this property.
43+
pub query_results: LatestAtResults,
44+
45+
/// Blueprint query used for querying.
46+
pub blueprint_query: LatestAtQuery,
4047
}
4148

4249
impl ViewProperty {

0 commit comments

Comments
 (0)