use std::collections::HashMap;
use egui::{Id, Ui, Vec2};
use crate::NodeId;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct DragState<NodeIdType> {
pub drag_overlay_offset: Vec2,
pub dragged: Vec<NodeIdType>,
pub simplified: Vec<NodeIdType>,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "persistence", derive(serde::Serialize, serde::Deserialize))]
pub struct TreeViewState<NodeIdType: Eq + std::hash::Hash> {
selected: Vec<NodeIdType>,
selection_pivot: Option<NodeIdType>,
selection_cursor: Option<NodeIdType>,
pub(crate) secondary_selection: Option<NodeIdType>,
#[cfg_attr(feature = "persistence", serde(skip))]
pub(crate) min_width: f32,
pub(crate) last_height: f32,
node_states: HashMap<NodeIdType, bool>,
pub(crate) last_clicked_node: Option<NodeIdType>,
dragged: Option<DragState<NodeIdType>>,
pub(crate) show_fallback_context_menu_for_selection: bool,
}
impl<NodeIdType: NodeId> Default for TreeViewState<NodeIdType> {
fn default() -> Self {
Self {
selected: Default::default(),
selection_pivot: None,
selection_cursor: None,
dragged: Default::default(),
secondary_selection: Default::default(),
min_width: 0.0,
last_height: 0.0,
node_states: HashMap::new(),
last_clicked_node: None,
show_fallback_context_menu_for_selection: false,
}
}
}
#[cfg(feature = "persistence")]
impl<NodeIdType> TreeViewState<NodeIdType>
where
NodeIdType: NodeId + Send + Sync + 'static,
{
pub fn load(ui: &mut Ui, id: Id) -> Option<Self> {
ui.data_mut(|d| d.get_persisted(id))
}
pub fn store(self, ui: &mut Ui, id: Id) {
ui.data_mut(|d| d.insert_persisted(id, self));
}
}
#[cfg(not(feature = "persistence"))]
impl<NodeIdType> TreeViewState<NodeIdType>
where
NodeIdType: NodeId + Send + Sync + 'static,
{
pub fn load(ui: &mut Ui, id: Id) -> Option<Self> {
ui.data_mut(|d| d.get_temp(id))
}
pub fn store(self, ui: &mut Ui, id: Id) {
ui.data_mut(|d| d.insert_temp(id, self));
}
}
impl<NodeIdType: NodeId> TreeViewState<NodeIdType> {
pub fn selected(&self) -> &Vec<NodeIdType> {
&self.selected
}
pub fn set_selected(&mut self, selected: Vec<NodeIdType>) {
self.selection_pivot = selected.first().cloned();
self.selected = selected;
}
pub fn set_one_selected(&mut self, selected: NodeIdType) {
self.selection_pivot = Some(selected.clone());
self.selected.clear();
self.selected.push(selected);
}
pub fn expand_parents_of(&mut self, _id: &NodeIdType) {
println!("TreeViewState::expand_parents_of not yet implemented");
}
pub fn expand_node(&mut self, _id: &NodeIdType) {
println!("TreeViewState::expand_node not yet implemented");
}
pub fn set_openness(&mut self, id: NodeIdType, open: bool) {
self.node_states.insert(id, open);
}
pub fn is_open(&self, id: &NodeIdType) -> Option<bool> {
self.node_states.get(id).cloned()
}
#[deprecated = "The TreeViewState no longer carries this information. Refer to your own data source"]
pub fn parent_id_of(&self, _id: &NodeIdType) -> Option<&NodeIdType> {
None
}
pub(crate) fn prune_selection_to_single_id(&mut self) {
if self.selected.len() > 1 {
if let Some(new_selection) = &self.selection_pivot {
self.set_one_selected(new_selection.clone());
}
}
}
pub(crate) fn get_simplified_dragged(&self) -> Option<&Vec<NodeIdType>> {
self.dragged.as_ref().map(|state| &state.simplified)
}
pub(crate) fn set_dragged(&mut self, dragged: DragState<NodeIdType>) {
self.dragged = Some(dragged);
}
pub(crate) fn reset_dragged(&mut self) {
self.dragged = None;
}
pub(crate) fn set_pivot(&mut self, id: Option<NodeIdType>) {
self.selection_pivot = id;
}
pub(crate) fn set_cursor(&mut self, id: Option<NodeIdType>) {
self.selection_cursor = id;
}
pub(crate) fn toggle_selected(&mut self, id: &NodeIdType) {
if self.selected.contains(id) {
self.selected.retain(|selected_id| selected_id != id);
} else {
self.selected.push(id.clone());
}
}
pub(crate) fn set_selected_dont_change_pivot(&mut self, selected: Vec<NodeIdType>) {
self.selected = selected;
}
pub(crate) fn get_dragged(&self) -> Option<&Vec<NodeIdType>> {
self.dragged.as_ref().map(|state| &state.dragged)
}
pub(crate) fn is_dragged(&self, id: &NodeIdType) -> bool {
self.dragged
.as_ref()
.is_some_and(|state| state.dragged.contains(id))
}
pub(crate) fn get_drag_overlay_offset(&self) -> Option<Vec2> {
self.dragged.as_ref().map(|d| d.drag_overlay_offset)
}
pub(crate) fn is_selected(&self, id: &NodeIdType) -> bool {
self.selected.contains(id)
}
pub(crate) fn is_secondary_selected(&self, id: &NodeIdType) -> bool {
self.secondary_selection.as_ref().is_some_and(|n| n == id)
}
pub(crate) fn selected_count(&self) -> usize {
self.selected.len()
}
pub(crate) fn is_selection_cursor(&self, id: &NodeIdType) -> bool {
self.selection_cursor
.as_ref()
.is_some_and(|cursor_id| cursor_id == id)
}
pub(crate) fn is_selection_pivot(&self, id: &NodeIdType) -> bool {
self.selection_pivot
.as_ref()
.is_some_and(|pivot_id| pivot_id == id)
}
pub(crate) fn was_clicked_last(&self, id: &NodeIdType) -> bool {
self.last_clicked_node
.as_ref()
.is_some_and(|last| last == id)
}
pub(crate) fn set_last_clicked(&mut self, id: &NodeIdType) {
self.last_clicked_node = Some(id.clone());
}
pub(crate) fn get_selection_cursor(&self) -> Option<&NodeIdType> {
self.selection_cursor.as_ref()
}
pub(crate) fn get_selection_pivot(&self) -> Option<&NodeIdType> {
self.selection_pivot.as_ref()
}
}