use re_data_store::{log_db::LogDb, InstancePath};
use re_log_types::{ComponentPath, EntityPath, MsgId, TimeInt, Timeline};
use crate::ui::{
data_ui::{ComponentUiRegistry, DataUi},
DataBlueprintGroupHandle, SpaceViewId, UiVerbosity,
};
use super::{
item::{Item, ItemCollection},
HoverHighlight,
};
pub struct ViewerContext<'a> {
pub app_options: &'a mut super::AppOptions,
pub cache: &'a mut super::Caches,
pub component_ui_registry: &'a ComponentUiRegistry,
pub log_db: &'a LogDb,
pub rec_cfg: &'a mut RecordingConfig,
pub re_ui: &'a re_ui::ReUi,
pub render_ctx: &'a mut re_renderer::RenderContext,
}
impl<'a> ViewerContext<'a> {
pub fn msg_id_button(&mut self, ui: &mut egui::Ui, msg_id: MsgId) -> egui::Response {
let selection = Item::MsgId(msg_id);
let response = ui
.selectable_label(self.selection().contains(&selection), msg_id.short_string())
.on_hover_ui(|ui| {
ui.label(format!("Message ID: {msg_id}"));
ui.separator();
msg_id.data_ui(self, ui, UiVerbosity::Small, &self.current_query());
});
self.cursor_interact_with_selectable(response, selection)
}
pub fn entity_path_button(
&mut self,
ui: &mut egui::Ui,
space_view_id: Option<SpaceViewId>,
entity_path: &EntityPath,
) -> egui::Response {
self.instance_path_button_to(
ui,
space_view_id,
&InstancePath::entity_splat(entity_path.clone()),
entity_path.to_string(),
)
}
pub fn entity_path_button_to(
&mut self,
ui: &mut egui::Ui,
space_view_id: Option<SpaceViewId>,
entity_path: &EntityPath,
text: impl Into<egui::WidgetText>,
) -> egui::Response {
self.instance_path_button_to(
ui,
space_view_id,
&InstancePath::entity_splat(entity_path.clone()),
text,
)
}
pub fn instance_path_button(
&mut self,
ui: &mut egui::Ui,
space_view_id: Option<SpaceViewId>,
instance_path: &InstancePath,
) -> egui::Response {
self.instance_path_button_to(ui, space_view_id, instance_path, instance_path.to_string())
}
pub fn instance_path_button_to(
&mut self,
ui: &mut egui::Ui,
space_view_id: Option<SpaceViewId>,
instance_path: &InstancePath,
text: impl Into<egui::WidgetText>,
) -> egui::Response {
let selection = Item::InstancePath(space_view_id, instance_path.clone());
let subtype_string = if instance_path.instance_key.is_splat() {
"Entity"
} else {
"Entity Instance"
};
let response = ui
.selectable_label(self.selection().contains(&selection), text)
.on_hover_ui(|ui| {
ui.strong(subtype_string);
ui.label(format!("Path: {instance_path}"));
instance_path.data_ui(
self,
ui,
crate::ui::UiVerbosity::Reduced,
&self.current_query(),
);
});
self.cursor_interact_with_selectable(response, selection)
}
pub fn component_path_button_to(
&mut self,
ui: &mut egui::Ui,
text: impl Into<egui::WidgetText>,
component_path: &ComponentPath,
) -> egui::Response {
let selection = Item::ComponentPath(component_path.clone());
let response = ui.selectable_label(self.selection().contains(&selection), text);
self.cursor_interact_with_selectable(response, selection)
}
pub fn space_view_button(
&mut self,
ui: &mut egui::Ui,
space_view: &crate::ui::SpaceView,
) -> egui::Response {
self.space_view_button_to(
ui,
space_view.display_name.clone(),
space_view.id,
space_view.category,
)
}
pub fn space_view_button_to(
&mut self,
ui: &mut egui::Ui,
text: impl Into<egui::WidgetText>,
space_view_id: SpaceViewId,
space_view_category: crate::ui::ViewCategory,
) -> egui::Response {
let selection = Item::SpaceView(space_view_id);
let is_selected = self.selection().contains(&selection);
let response = self
.re_ui
.selectable_label_with_icon(ui, space_view_category.icon(), text, is_selected)
.on_hover_text("Space View");
self.cursor_interact_with_selectable(response, selection)
}
pub fn data_blueprint_group_button_to(
&mut self,
ui: &mut egui::Ui,
text: impl Into<egui::WidgetText>,
space_view_id: SpaceViewId,
group_handle: DataBlueprintGroupHandle,
) -> egui::Response {
let selection = Item::DataBlueprintGroup(space_view_id, group_handle);
let response = self
.re_ui
.selectable_label_with_icon(
ui,
&re_ui::icons::CONTAINER,
text,
self.selection().contains(&selection),
)
.on_hover_text("Group");
self.cursor_interact_with_selectable(response, selection)
}
pub fn data_blueprint_button_to(
&mut self,
ui: &mut egui::Ui,
text: impl Into<egui::WidgetText>,
space_view_id: SpaceViewId,
entity_path: &EntityPath,
) -> egui::Response {
let selection = Item::InstancePath(
Some(space_view_id),
InstancePath::entity_splat(entity_path.clone()),
);
let response = ui
.selectable_label(self.selection().contains(&selection), text)
.on_hover_ui(|ui| {
ui.strong("Space View Entity");
ui.label(format!("Path: {entity_path}"));
entity_path.data_ui(self, ui, UiVerbosity::Reduced, &self.current_query());
});
self.cursor_interact_with_selectable(response, selection)
}
pub fn time_button(
&mut self,
ui: &mut egui::Ui,
timeline: &Timeline,
value: TimeInt,
) -> egui::Response {
let is_selected = self.rec_cfg.time_ctrl.is_time_selected(timeline, value);
let response = ui.selectable_label(is_selected, timeline.typ().format(value));
if response.clicked() {
self.rec_cfg
.time_ctrl
.set_timeline_and_time(*timeline, value);
self.rec_cfg.time_ctrl.pause();
}
response
}
pub fn timeline_button(&mut self, ui: &mut egui::Ui, timeline: &Timeline) -> egui::Response {
self.timeline_button_to(ui, timeline.name().to_string(), timeline)
}
pub fn timeline_button_to(
&mut self,
ui: &mut egui::Ui,
text: impl Into<egui::WidgetText>,
timeline: &Timeline,
) -> egui::Response {
let is_selected = self.rec_cfg.time_ctrl.timeline() == timeline;
let response = ui
.selectable_label(is_selected, text)
.on_hover_text("Click to switch to this timeline");
if response.clicked() {
self.rec_cfg.time_ctrl.set_timeline(*timeline);
self.rec_cfg.time_ctrl.pause();
}
response
}
pub fn set_single_selection(&mut self, item: Item) -> ItemCollection {
self.rec_cfg.selection_state.set_single_selection(item)
}
pub fn set_multi_selection(&mut self, items: impl Iterator<Item = Item>) -> ItemCollection {
self.rec_cfg.selection_state.set_multi_selection(items)
}
pub fn select_hovered_on_click(&mut self, response: &egui::Response) {
if response.clicked() {
let hovered = self.rec_cfg.selection_state.hovered().clone();
if response.ctx.input(|i| i.modifiers.command) {
self.rec_cfg
.selection_state
.toggle_selection(hovered.to_vec());
} else {
self.set_multi_selection(hovered.into_iter());
}
}
}
pub fn cursor_interact_with_selectable(
&mut self,
response: egui::Response,
selectable: Item,
) -> egui::Response {
let is_item_hovered =
self.selection_state().highlight_for_ui_element(&selectable) == HoverHighlight::Hovered;
if response.hovered() {
self.rec_cfg
.selection_state
.set_hovered(std::iter::once(selectable));
}
self.select_hovered_on_click(&response);
if is_item_hovered {
response.highlight()
} else {
response
}
}
pub fn selection(&self) -> &ItemCollection {
self.rec_cfg.selection_state.current()
}
pub fn hovered(&self) -> &ItemCollection {
self.rec_cfg.selection_state.hovered()
}
pub fn set_hovered(&mut self, hovered: impl Iterator<Item = Item>) {
self.rec_cfg.selection_state.set_hovered(hovered);
}
pub fn selection_state(&self) -> &super::SelectionState {
&self.rec_cfg.selection_state
}
pub fn selection_state_mut(&mut self) -> &mut super::SelectionState {
&mut self.rec_cfg.selection_state
}
pub fn current_query(&self) -> re_arrow_store::LatestAtQuery {
self.rec_cfg.time_ctrl.current_query()
}
}
#[derive(Default, serde::Deserialize, serde::Serialize)]
#[serde(default)]
pub struct RecordingConfig {
pub time_ctrl: crate::TimeControl,
pub selection_state: super::SelectionState,
}