use std::collections::HashSet;
use crate::dataframe_ui::HideColumnAction;
use crate::view_query::Query;
use re_chunk_store::{ColumnDescriptor, ColumnSelector, ComponentColumnSelector};
use re_log_types::{EntityPath, ResolvedTimeRange, TimelineName};
use re_types::blueprint::{components, datatypes};
use re_viewer_context::{SpaceViewSystemExecutionError, ViewerContext};
impl Query {
pub(crate) fn timeline(
&self,
ctx: &ViewerContext<'_>,
) -> Result<re_log_types::Timeline, SpaceViewSystemExecutionError> {
let timeline = self
.query_property
.component_or_empty::<components::TimelineName>()?
.and_then(|name| {
ctx.recording()
.timelines()
.find(|timeline| timeline.name() == &TimelineName::from(name.as_str()))
.copied()
});
let save_timeline = timeline.is_none();
let timeline = timeline.unwrap_or_else(|| *ctx.rec_cfg.time_ctrl.read().timeline());
if save_timeline {
self.save_timeline_name(ctx, timeline.name());
}
Ok(timeline)
}
pub(super) fn save_timeline_name(&self, ctx: &ViewerContext<'_>, timeline_name: &TimelineName) {
self.query_property
.save_blueprint_component(ctx, &components::TimelineName::from(timeline_name.as_str()));
self.query_property
.clear_blueprint_component::<components::FilterByRange>(ctx);
}
pub(crate) fn filter_by_range(
&self,
) -> Result<ResolvedTimeRange, SpaceViewSystemExecutionError> {
#[allow(clippy::map_unwrap_or)]
Ok(self
.query_property
.component_or_empty::<components::FilterByRange>()?
.map(|range_filter| (ResolvedTimeRange::new(range_filter.start, range_filter.end)))
.unwrap_or(ResolvedTimeRange::EVERYTHING))
}
pub(super) fn save_filter_by_range(&self, ctx: &ViewerContext<'_>, range: ResolvedTimeRange) {
if range == ResolvedTimeRange::EVERYTHING {
self.query_property
.clear_blueprint_component::<components::FilterByRange>(ctx);
} else {
self.query_property.save_blueprint_component(
ctx,
&components::FilterByRange::new(range.min(), range.max()),
);
}
}
pub(crate) fn filter_is_not_null(
&self,
) -> Result<Option<ComponentColumnSelector>, SpaceViewSystemExecutionError> {
Ok(self
.filter_is_not_null_raw()?
.filter(|filter_is_not_null| filter_is_not_null.active())
.map(|filter| {
ComponentColumnSelector::new_for_component_name(
filter.entity_path(),
filter.component_name(),
)
}))
}
pub(super) fn filter_is_not_null_raw(
&self,
) -> Result<Option<components::FilterIsNotNull>, SpaceViewSystemExecutionError> {
Ok(self
.query_property
.component_or_empty::<components::FilterIsNotNull>()?)
}
pub(super) fn save_filter_is_not_null(
&self,
ctx: &ViewerContext<'_>,
filter_is_not_null: &components::FilterIsNotNull,
) {
self.query_property
.save_blueprint_component(ctx, filter_is_not_null);
}
pub(crate) fn latest_at_enabled(&self) -> Result<bool, SpaceViewSystemExecutionError> {
Ok(self
.query_property
.component_or_empty::<components::ApplyLatestAt>()?
.map_or(false, |comp| *comp.0))
}
pub(crate) fn save_latest_at_enabled(&self, ctx: &ViewerContext<'_>, enabled: bool) {
self.query_property
.save_blueprint_component(ctx, &components::ApplyLatestAt(enabled.into()));
}
pub(super) fn save_selected_columns(
&self,
ctx: &ViewerContext<'_>,
columns: impl IntoIterator<Item = ColumnSelector>,
) {
let mut selected_columns = datatypes::SelectedColumns::default();
for column in columns {
match column {
ColumnSelector::Time(desc) => {
selected_columns
.time_columns
.push(desc.timeline.as_str().into());
}
ColumnSelector::Component(selector) => {
let blueprint_component_selector = datatypes::ComponentColumnSelector::new(
&selector.entity_path,
&selector.component_name,
);
selected_columns
.component_columns
.push(blueprint_component_selector);
}
}
}
self.query_property
.save_blueprint_component(ctx, &components::SelectedColumns(selected_columns));
}
pub(super) fn save_all_columns_selected(&self, ctx: &ViewerContext<'_>) {
self.query_property
.clear_blueprint_component::<components::SelectedColumns>(ctx);
}
pub(super) fn save_all_columns_unselected(&self, ctx: &ViewerContext<'_>) {
self.query_property
.save_blueprint_component(ctx, &components::SelectedColumns::default());
}
pub(crate) fn apply_column_visibility_to_view_columns(
&self,
ctx: &ViewerContext<'_>,
view_columns: &[ColumnDescriptor],
) -> Result<Option<Vec<ColumnSelector>>, SpaceViewSystemExecutionError> {
let selected_columns = self
.query_property
.component_or_empty::<components::SelectedColumns>()?;
let Some(datatypes::SelectedColumns {
time_columns,
component_columns,
}) = selected_columns.as_deref()
else {
return Ok(None);
};
let selected_time_columns: HashSet<TimelineName> = time_columns
.iter()
.map(|timeline_name| timeline_name.as_str().into())
.collect();
let selected_component_columns = component_columns
.iter()
.map(|selector| {
(
EntityPath::from(selector.entity_path.as_str()),
selector.component.as_str(),
)
})
.collect::<HashSet<_>>();
let query_timeline_name = *self.timeline(ctx)?.name();
let result = view_columns
.iter()
.filter(|column| match column {
ColumnDescriptor::Time(desc) => {
desc.timeline.name() == &query_timeline_name
|| selected_time_columns.contains(desc.timeline.name())
}
ColumnDescriptor::Component(desc) => {
selected_component_columns
.contains(&(desc.entity_path.clone(), desc.component_name.full_name()))
|| selected_component_columns
.contains(&(desc.entity_path.clone(), desc.component_name.short_name()))
}
})
.cloned()
.map(ColumnSelector::from)
.collect();
Ok(Some(result))
}
pub(crate) fn handle_hide_column_actions(
&self,
ctx: &ViewerContext<'_>,
view_columns: &[ColumnDescriptor],
actions: Vec<HideColumnAction>,
) -> Result<(), SpaceViewSystemExecutionError> {
if actions.is_empty() {
return Ok(());
}
let mut selected_columns: Vec<_> = self
.apply_column_visibility_to_view_columns(ctx, view_columns)?
.map(|columns| columns.into_iter().collect())
.unwrap_or_else(|| view_columns.iter().cloned().map(Into::into).collect());
for action in actions {
match action {
HideColumnAction::HideTimeColumn { timeline_name } => {
selected_columns.retain(|column| match column {
ColumnSelector::Time(desc) => desc.timeline != timeline_name,
ColumnSelector::Component(_) => true,
});
}
HideColumnAction::HideComponentColumn {
entity_path,
component_name,
} => {
selected_columns.retain(|column| match column {
ColumnSelector::Component(selector) => {
selector.entity_path != entity_path
|| !component_name.matches(&selector.component_name)
}
ColumnSelector::Time(_) => true,
});
}
}
}
self.save_selected_columns(ctx, selected_columns);
Ok(())
}
}
#[cfg(test)]
mod test {
use super::Query;
use re_viewer_context::test_context::TestContext;
use re_viewer_context::SpaceViewId;
#[test]
fn test_latest_at_enabled() {
let mut test_context = TestContext::default();
let view_id = SpaceViewId::random();
test_context.run_and_handle_system_commands(|ctx, _| {
let query = Query::from_blueprint(ctx, view_id);
query.save_latest_at_enabled(ctx, true);
});
test_context.run(|ctx, _| {
let query = Query::from_blueprint(ctx, view_id);
assert!(query.latest_at_enabled().unwrap());
});
}
}