1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//! Rerun Data Ui
//!
//! This crate provides ui elements for Rerun component data for the Rerun Viewer.

use itertools::Itertools;

use re_log_types::{DataCell, EntityPath, TimePoint};
use re_types::ComponentName;
use re_viewer_context::{UiVerbosity, ViewerContext};

mod annotation_context;
mod app_id;
mod blueprint_data;
mod component;
mod component_path;
mod component_ui_registry;
mod data;
mod data_source;
mod editors;
mod entity_db;
mod entity_path;
mod image;
mod image_meaning;
mod instance_path;
mod log_msg;
mod material;
mod pinhole;
mod rotation3d;
mod store_id;
mod transform3d;

pub mod item_ui;

pub use crate::image::{
    show_zoomed_image_region, show_zoomed_image_region_area_outline,
    tensor_summary_ui_grid_contents,
};
pub use component::EntityComponentWithInstances;
pub use component_ui_registry::{add_to_registry, create_component_ui_registry};
pub use image_meaning::image_meaning_for_entity;

/// Filter out components that should not be shown in the UI,
/// and order the other components in a cosnsiten way.
pub fn ui_visible_components<'a>(
    iter: impl IntoIterator<Item = &'a ComponentName> + 'a,
) -> Vec<ComponentName> {
    let mut components: Vec<ComponentName> = iter
        .into_iter()
        .cloned()
        .filter(is_component_visible_in_ui)
        .collect();

    // Put indicator components first:
    components.sort_by_key(|c| (!c.is_indicator_component(), c.full_name()));

    components
}

/// Show this component in the UI.
pub fn is_component_visible_in_ui(component_name: &ComponentName) -> bool {
    const HIDDEN_COMPONENTS: &[&str] = &["rerun.components.InstanceKey"];
    !HIDDEN_COMPONENTS.contains(&component_name.as_ref())
}

/// Types implementing [`DataUi`] can display themselves in an [`egui::Ui`].
pub trait DataUi {
    /// If you need to lookup something in the data store, use the given query to do so.
    fn data_ui(
        &self,
        ctx: &ViewerContext<'_>,
        ui: &mut egui::Ui,
        verbosity: UiVerbosity,
        query: &re_data_store::LatestAtQuery,
        store: &re_data_store::DataStore,
    );
}

/// Similar to [`DataUi`], but for data that is related to an entity (e.g. a component).
///
/// This is given the context of the entity it is part of so it can do queries.
pub trait EntityDataUi {
    /// If you need to lookup something in the data store, use the given query to do so.
    fn entity_data_ui(
        &self,
        ctx: &ViewerContext<'_>,
        ui: &mut egui::Ui,
        verbosity: UiVerbosity,
        entity_path: &EntityPath,
        query: &re_data_store::LatestAtQuery,
        store: &re_data_store::DataStore,
    );
}

impl<T> EntityDataUi for T
where
    T: DataUi,
{
    fn entity_data_ui(
        &self,
        ctx: &ViewerContext<'_>,
        ui: &mut egui::Ui,
        verbosity: UiVerbosity,
        entity: &EntityPath,
        query: &re_data_store::LatestAtQuery,
        store: &re_data_store::DataStore,
    ) {
        // This ensures that UI state is maintained per entity. For example, the collapsed state for
        // `AnnotationContext` component is not saved by all instances of the component.
        ui.push_id(entity.hash(), |ui| {
            self.data_ui(ctx, ui, verbosity, query, store);
        });
    }
}

// ----------------------------------------------------------------------------

impl DataUi for TimePoint {
    fn data_ui(
        &self,
        ctx: &ViewerContext<'_>,
        ui: &mut egui::Ui,
        _verbosity: UiVerbosity,
        _query: &re_data_store::LatestAtQuery,
        _store: &re_data_store::DataStore,
    ) {
        ui.vertical(|ui| {
            egui::Grid::new("time_point").num_columns(2).show(ui, |ui| {
                ui.spacing_mut().item_spacing.x = 0.0;
                for (timeline, value) in self.iter() {
                    item_ui::timeline_button_to(ctx, ui, format!("{}:", timeline.name()), timeline);
                    item_ui::time_button(ctx, ui, timeline, *value);
                    ui.end_row();
                }
            });
        });
    }
}

impl DataUi for [DataCell] {
    fn data_ui(
        &self,
        _ctx: &ViewerContext<'_>,
        ui: &mut egui::Ui,
        verbosity: UiVerbosity,
        _query: &re_data_store::LatestAtQuery,
        _store: &re_data_store::DataStore,
    ) {
        let mut sorted = self.to_vec();
        sorted.sort_by_key(|cb| cb.component_name());

        match verbosity {
            UiVerbosity::Small => {
                ui.label(sorted.iter().map(format_cell).join(", "));
            }

            UiVerbosity::Full | UiVerbosity::LimitHeight | UiVerbosity::Reduced => {
                ui.vertical(|ui| {
                    for component_bundle in &sorted {
                        ui.label(format_cell(component_bundle));
                    }
                });
            }
        }
    }
}

fn format_cell(cell: &DataCell) -> String {
    // TODO(emilk): if there's only once instance, and the byte size is small, then deserialize and show the value.
    format!(
        "{}x {}",
        cell.num_instances(),
        cell.component_name().short_name()
    )
}

// ---------------------------------------------------------------------------

pub fn annotations(
    ctx: &ViewerContext<'_>,
    query: &re_data_store::LatestAtQuery,
    entity_path: &re_entity_db::EntityPath,
) -> std::sync::Arc<re_viewer_context::Annotations> {
    re_tracing::profile_function!();
    let mut annotation_map = re_viewer_context::AnnotationMap::default();
    annotation_map.load(ctx, query, std::iter::once(entity_path));
    annotation_map.find(entity_path)
}

// ---------------------------------------------------------------------------

/// Build an egui table and configure it for the given verbosity.
///
/// Note that the caller is responsible for strictly limiting the number of displayed rows for
/// [`UiVerbosity::Small`] and [`UiVerbosity::Reduced`], as the table will not scroll.
pub fn table_for_verbosity(
    verbosity: UiVerbosity,
    ui: &mut egui::Ui,
) -> egui_extras::TableBuilder<'_> {
    let table = egui_extras::TableBuilder::new(ui);
    match verbosity {
        UiVerbosity::Small | UiVerbosity::Reduced => {
            // Be as small as possible in the hover tooltips. No scrolling related configuration, as
            // the content itself must be limited (scrolling is not possible in tooltips).
            table.auto_shrink([true, true])
        }
        UiVerbosity::LimitHeight => {
            // Don't take too much vertical space to leave room for other selected items.
            table
                .auto_shrink([false, true])
                .vscroll(true)
                .max_scroll_height(100.0)
        }
        UiVerbosity::Full => {
            // We're alone in the selection panel. Let the outer ScrollArea do the work.
            table.auto_shrink([false, true]).vscroll(false)
        }
    }
}