re_data_ui/
latest_at_instance_ui.rs1use egui::NumExt as _;
2use re_chunk_store::UnitChunkShared;
3use re_entity_db::InstancePath;
4use re_format::format_plural_s;
5use re_log_types::{EntityPath, Instance, TimePoint};
6use re_sdk_types::ComponentIdentifier;
7use re_ui::{SyntaxHighlighting as _, UiExt as _};
8use re_viewer_context::{StoreViewContext, UiLayout};
9
10use crate::item_ui;
11
12use super::DataUi;
13
14#[derive(Clone)]
16pub struct LatestAtInstanceResult<'a> {
17 pub entity_path: EntityPath,
19
20 pub component: ComponentIdentifier,
22
23 pub instance: Instance,
25
26 pub unit: &'a UnitChunkShared,
27}
28
29impl DataUi for LatestAtInstanceResult<'_> {
30 fn data_ui(&self, ctx: &StoreViewContext<'_>, ui: &mut egui::Ui, ui_layout: UiLayout) {
31 let Self {
32 entity_path,
33 component,
34 instance,
35 unit,
36 } = self.clone();
37
38 re_tracing::profile_function!(component);
39
40 ui.sanity_check();
41
42 let tokens = ui.tokens();
43
44 let engine = ctx.db.storage_engine();
45
46 let Some(component_descriptor) = engine
47 .schema()
48 .entity_component_descriptor(&entity_path, component)
49 else {
50 ui.label(format!("Entity {entity_path} has no component {component}"));
51 return;
52 };
53
54 let num_instances = unit.num_instances(component);
55
56 let max_row = match ui_layout {
58 UiLayout::List | UiLayout::Inline => 0,
59 UiLayout::Tooltip => num_instances.at_most(4), UiLayout::SelectionPanel => num_instances,
61 };
62
63 let num_displayed_rows = if num_instances <= max_row {
81 num_instances
82 } else {
83 max_row.saturating_sub(1)
85 };
86
87 if num_instances == 1 || instance.is_specific() {
88 if num_instances == 1
90 && entity_path.starts_with(&EntityPath::properties())
91 && let Some(array) = unit.component_batch_raw(component)
92 && ctx.app_ctx.component_ui_registry.try_show_edit_ui(
93 ctx,
94 ui,
95 re_viewer_context::EditTarget {
96 store_id: ctx.db.store_id().clone(),
97 timepoint: TimePoint::STATIC,
98 entity_path: entity_path.clone(),
99 },
100 array.as_ref(),
101 component_descriptor.clone(),
102 !ui_layout.is_single_line(),
103 ) != re_viewer_context::TryShowEditUiResult::NotShown
104 {
105 return;
106 }
107
108 ctx.app_ctx.component_ui_registry.component_ui(
109 ctx,
110 ui,
111 ui_layout,
112 &entity_path,
113 &component_descriptor,
114 unit,
115 &instance,
116 );
117 } else if ui_layout.is_single_line() {
118 ui.label(format_plural_s(num_instances, "value"));
119 } else {
120 let table_style = re_ui::TableStyle::Dense;
121 ui_layout
122 .table(ui)
123 .resizable(false)
124 .cell_layout(egui::Layout::left_to_right(egui::Align::Center))
125 .column(egui_extras::Column::auto())
126 .column(egui_extras::Column::remainder())
127 .header(tokens.deprecated_table_header_height(), |mut header| {
128 re_ui::DesignTokens::setup_table_header(&mut header);
129 header.col(|ui| {
130 ui.label("Index");
131 });
132 header.col(|ui| {
133 ui.label(component_descriptor.display_name());
134 });
135 })
136 .body(|mut body| {
137 tokens.setup_table_body(&mut body, table_style);
138 let row_height = tokens.table_row_height(table_style);
139 body.rows(row_height, num_displayed_rows as _, |mut row| {
140 let instance = Instance::from(row.index() as u64);
141 row.col(|ui| {
142 let instance_text = instance.syntax_highlighted(ui.style());
143 if ui.is_tooltip() {
144 ui.label(instance_text);
147 } else {
148 let instance_path =
149 InstancePath::instance(entity_path.clone(), instance);
150 item_ui::instance_path_button_to(
151 ctx,
152 ui,
153 None,
154 &instance_path,
155 instance_text,
156 );
157 }
158 });
159 row.col(|ui| {
160 ctx.app_ctx.component_ui_registry.component_ui(
161 ctx,
162 ui,
163 UiLayout::List,
164 &entity_path,
165 &component_descriptor,
166 unit,
167 &instance,
168 );
169 });
170 });
171 });
172
173 if num_instances > num_displayed_rows {
174 ui.label(format!(
175 "…and {} more.",
176 re_format::format_uint(num_instances - num_displayed_rows)
177 ));
178 }
179 }
180
181 ui.sanity_check();
182 }
183}