use re_mutex::Mutex;
use super::Item;
use crate::command_sender::{SelectionSource, SetSelection};
use crate::{DataResultInteractionAddress, ItemCollection, ItemContext};
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum SelectionHighlight {
#[default]
None,
SiblingSelection,
Selection,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum HoverHighlight {
#[default]
None,
Hovered,
}
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
pub struct InteractionHighlight {
pub selection: SelectionHighlight,
pub hover: HoverHighlight,
}
impl InteractionHighlight {
#[inline]
pub fn max(&self, other: Self) -> Self {
Self {
selection: self.selection.max(other.selection),
hover: self.hover.max(other.hover),
}
}
pub fn any(&self) -> bool {
self.selection != SelectionHighlight::None || self.hover != HoverHighlight::None
}
}
#[derive(Default)]
pub struct ApplicationSelectionState {
selection: ItemCollection,
selection_changed: Option<SelectionSource>,
hovered_previous_frame: ItemCollection,
hovered_this_frame: Mutex<ItemCollection>,
}
pub enum SelectionChange<'a> {
NoChange,
SelectionChanged(&'a ItemCollection),
}
impl ApplicationSelectionState {
pub fn on_frame_start(
&mut self,
item_retain_condition: impl Fn(&Item) -> bool,
fallback_selection: Option<Item>,
) -> SelectionChange<'_> {
re_tracing::profile_scope!("SelectionState::on_frame_start");
let start_len = self.selection.len();
self.selection.retain(|item, _| item_retain_condition(item));
if start_len != self.selection.len() {
self.selection_changed = Some(SelectionSource::Other);
}
if self.selection.is_empty()
&& let Some(fallback_selection) = fallback_selection
{
re_log::trace!("Current selection invalid in this context; switching to fallback");
self.selection = ItemCollection::from(fallback_selection);
}
self.hovered_previous_frame = std::mem::take(self.hovered_this_frame.get_mut());
if self.selection_changed.is_some() {
SelectionChange::SelectionChanged(&self.selection)
} else {
SelectionChange::NoChange
}
}
pub fn on_frame_end(&mut self) {
self.selection_changed = None;
}
pub fn set_selection(&mut self, items: impl Into<SetSelection>) {
let SetSelection { selection, source } = items.into();
if selection != self.selection {
self.selection_changed = Some(source);
self.selection = selection;
}
}
pub fn selected_items(&self) -> &ItemCollection {
&self.selection
}
pub fn hovered_items(&self) -> &ItemCollection {
&self.hovered_previous_frame
}
pub fn set_hovered(&self, hovered: impl Into<ItemCollection>) {
*self.hovered_this_frame.lock() = hovered.into();
}
pub fn selection_item_contexts(&self) -> impl Iterator<Item = &ItemContext> {
self.selection.iter_item_context()
}
pub fn hovered_item_context(&self) -> Option<&ItemContext> {
self.hovered_previous_frame.iter_item_context().next()
}
pub fn selection_changed(&self) -> Option<SelectionSource> {
self.selection_changed
}
pub fn highlight_for_ui_element(&self, test: &Item) -> HoverHighlight {
let hovered = self
.hovered_previous_frame
.iter_items()
.any(|current| match current {
Item::AppId(_)
| Item::TableId(_)
| Item::DataSource(_)
| Item::StoreId(_)
| Item::View(_)
| Item::Container(_)
| Item::RedapEntry(_)
| Item::RedapServer(_) => current == test,
Item::ComponentPath(component_path) => match test {
Item::AppId(_)
| Item::TableId(_)
| Item::DataSource(_)
| Item::StoreId(_)
| Item::View(_)
| Item::Container(_)
| Item::RedapEntry(_)
| Item::RedapServer(_) => false,
Item::ComponentPath(test_component_path) => {
test_component_path == component_path
}
Item::InstancePath(test_instance_path) => {
!test_instance_path.instance.is_specific()
&& test_instance_path.entity_path == component_path.entity_path
}
Item::DataResult(test_data_result) => {
test_data_result.instance_path.entity_path == component_path.entity_path
}
},
Item::InstancePath(current_instance_path) => match test {
Item::AppId(_)
| Item::TableId(_)
| Item::DataSource(_)
| Item::StoreId(_)
| Item::ComponentPath(_)
| Item::View(_)
| Item::Container(_)
| Item::RedapEntry(_)
| Item::RedapServer(_) => false,
Item::InstancePath(instance_path)
| Item::DataResult(DataResultInteractionAddress { instance_path, .. }) => {
current_instance_path.entity_path == instance_path.entity_path
&& either_none_or_same(
¤t_instance_path.instance.specific_index(),
&instance_path.instance.specific_index(),
)
}
},
Item::DataResult(current_data_result) => match test {
Item::AppId(_)
| Item::TableId(_)
| Item::DataSource(_)
| Item::StoreId(_)
| Item::ComponentPath(_)
| Item::View(_)
| Item::Container(_)
| Item::RedapEntry(_)
| Item::RedapServer(_) => false,
Item::InstancePath(instance_path)
| Item::DataResult(DataResultInteractionAddress { instance_path, .. }) => {
current_data_result.instance_path.entity_path == instance_path.entity_path
&& either_none_or_same(
¤t_data_result.instance_path.instance.specific_index(),
&instance_path.instance.specific_index(),
)
}
},
});
if hovered {
HoverHighlight::Hovered
} else {
HoverHighlight::None
}
}
}
fn either_none_or_same<T: PartialEq>(a: &Option<T>, b: &Option<T>) -> bool {
a.is_none() || b.is_none() || a == b
}