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
//! 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, SpaceViewId};

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

    /// Blueprint tree from the blueprint panel
    BlueprintTree,
}

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

    // convenience functions

    /// 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 space view in this scope.
    pub fn space_view(self, space_view_id: SpaceViewId) -> CollapsedId {
        CollapsedId {
            item: CollapseItem::SpaceView(space_view_id),
            scope: self,
        }
    }

    /// Create a [`CollapsedId`] for a data result in this scope.
    pub fn data_result(self, space_view_id: SpaceViewId, entity_path: EntityPath) -> CollapsedId {
        CollapsedId {
            item: CollapseItem::DataResult(space_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),
    SpaceView(SpaceViewId),
    DataResult(SpaceViewId, 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);
    }
}