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
//! Helper types for producing stable [`egui::Id`] for the purpose of handling collapsed state of
//! various UI elements.

use std::hash::Hash;

use re_log_types::EntityPath;

use crate::{ContainerId, Item, ViewId};

/// The various scopes for which we want to track collapsed state.
#[derive(Debug, Clone, Copy, Hash)]
#[allow(clippy::enum_variant_names)]
pub enum CollapseScope {
    /// Stream tree from the time panel
    StreamsTree,

    /// Stream tree from the time panel, when the filter is active
    StreamsTreeFiltered { session_id: egui::Id },

    /// The stream tree from the blueprint debug time panel
    BlueprintStreamsTree,

    /// The stream tree from the blueprint debug time panel, when the filter is active
    BlueprintStreamsTreeFiltered { session_id: egui::Id },

    /// Blueprint tree from the blueprint panel (left panel)
    BlueprintTree,

    /// Blueprint tree from the blueprint panel (left panel), when the filter is active
    BlueprintTreeFiltered { session_id: egui::Id },
}

impl CollapseScope {
    const ALL: [Self; 2] = [Self::StreamsTree, Self::BlueprintTree];

    // convenience functions

    /// Create a [`CollapsedId`] for an [`Item`] of supported kind.
    pub fn item(self, item: Item) -> Option<CollapsedId> {
        match item {
            Item::InstancePath(instance_path) => Some(self.entity(instance_path.entity_path)),
            Item::Container(container_id) => Some(self.container(container_id)),
            Item::View(view_id) => Some(self.view(view_id)),
            Item::DataResult(view_id, instance_path) => {
                Some(self.data_result(view_id, instance_path.entity_path))
            }

            Item::AppId(_) | Item::DataSource(_) | Item::StoreId(_) | Item::ComponentPath(_) => {
                None
            }
        }
    }

    /// Create a [`CollapsedId`] for a container in this scope.
    pub fn container(self, container_id: ContainerId) -> CollapsedId {
        CollapsedId {
            item: CollapseItem::Container(container_id),
            scope: self,
        }
    }

    /// Create a [`CollapsedId`] for a view in this scope.
    pub fn view(self, view_id: ViewId) -> CollapsedId {
        CollapsedId {
            item: CollapseItem::View(view_id),
            scope: self,
        }
    }

    /// Create a [`CollapsedId`] for a data result in this scope.
    pub fn data_result(self, view_id: ViewId, entity_path: EntityPath) -> CollapsedId {
        CollapsedId {
            item: CollapseItem::DataResult(view_id, entity_path),
            scope: self,
        }
    }

    /// Create a [`CollapsedId`] for an entity in this scope.
    pub fn entity(self, entity_path: EntityPath) -> CollapsedId {
        CollapsedId {
            item: CollapseItem::Entity(entity_path),
            scope: self,
        }
    }
}

/// The various kinds of items that may be represented and for which we want to track the collapsed
/// state.
#[derive(Debug, Clone, Hash)]
pub enum CollapseItem {
    Container(ContainerId),
    View(ViewId),
    DataResult(ViewId, EntityPath),
    Entity(EntityPath),
}

impl CollapseItem {
    /// Set the collapsed state for the given item in every available scopes.
    pub fn set_open_all(&self, ctx: &egui::Context, open: bool) {
        for scope in CollapseScope::ALL {
            let id = CollapsedId {
                item: self.clone(),
                scope,
            };
            id.set_open(ctx, open);
        }
    }
}

/// A collapsed identifier.
///
/// A `CollapsedId` resolves into a stable [`egui::Id`] for a given item and scope.
#[derive(Debug, Clone, Hash)]
pub struct CollapsedId {
    item: CollapseItem,
    scope: CollapseScope,
}

impl From<CollapsedId> for egui::Id {
    fn from(id: CollapsedId) -> Self {
        Self::new(id)
    }
}

impl CollapsedId {
    /// Convert to an [`egui::Id`].
    pub fn egui_id(&self) -> egui::Id {
        self.clone().into()
    }

    /// Check the collapsed state for the given [`CollapsedId`].
    pub fn is_open(&self, ctx: &egui::Context) -> Option<bool> {
        egui::collapsing_header::CollapsingState::load(ctx, self.egui_id())
            .map(|state| state.is_open())
    }

    /// Set the collapsed state for the given [`CollapsedId`].
    pub fn set_open(&self, ctx: &egui::Context, open: bool) {
        let mut collapsing_state = egui::collapsing_header::CollapsingState::load_with_default_open(
            ctx,
            self.egui_id(),
            false,
        );
        collapsing_state.set_open(open);
        collapsing_state.store(ctx);
    }
}