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 148 149 150 151 152
use std::collections::BTreeMap;
/// Storage for [`ExpandedRows`], which should be persisted across frames.
///
/// Note: each view should store its own cache. Using a [`re_viewer_context::SpaceViewState`] is a
/// good way to do this.
#[derive(Debug, Clone)]
pub(crate) struct ExpandedRowsCache {
/// Maps "table row number" to "additional lines".
///
/// When expanded, the base space is still used for the summary, while the additional lines are
/// used for instances.
expanded_rows: BTreeMap<u64, u64>,
/// ID used to invalidate the cache.
valid_for: egui::Id,
}
impl Default for ExpandedRowsCache {
fn default() -> Self {
Self {
expanded_rows: BTreeMap::default(),
valid_for: egui::Id::new(""),
}
}
}
impl ExpandedRowsCache {
/// This sets the query used for cache invalidation.
///
/// If the query doesn't match the cached one, the state will be reset.
fn validate_id(&mut self, id: egui::Id) {
if id != self.valid_for {
self.valid_for = id;
self.expanded_rows = BTreeMap::default();
}
}
}
/// Helper to keep track of row expansion.
///
/// This is a short-lived struct to be created every frame. The persistent state is stored in
/// [`ExpandedRowsCache`].
///
/// Uses egui's animation support to animate the row expansion/contraction. For this to work:
/// - When collapsed, the row entry must be set to 0 instead of being removed. Otherwise, it will no
/// longer be "seen" by the animation code. Technically, it could be removed _after_ the
/// animation completes, but it's not worth the complexity.
/// - When the row is first expanded, for the animation to work, it must be immediately seeded to 0
/// for the animation to have a starting point.
pub(crate) struct ExpandedRows<'a> {
/// Base row height.
row_height: f32,
/// Cache containing the row expanded-ness.
cache: &'a mut ExpandedRowsCache,
/// [`egui::Context`] used to animate the row expansion.
egui_ctx: egui::Context,
/// [`egui::Id`] used to store the animation state.
id: egui::Id,
}
impl<'a> ExpandedRows<'a> {
/// Create a new [`ExpandedRows`] instance.
///
/// `egui_ctx` is used to animate the row expansion
/// `id` is used to store the animation state and invalidate the cache, make it persistent and
/// unique
pub(crate) fn new(
egui_ctx: egui::Context,
id: egui::Id,
cache: &'a mut ExpandedRowsCache,
row_height: f32,
) -> Self {
// (in-)validate the cache
cache.validate_id(id);
Self {
row_height,
cache,
egui_ctx,
id,
}
}
/// Implementation for [`egui_table::TableDelegate::row_top_offset`].
pub(crate) fn row_top_offset(&self, row_nr: u64) -> f32 {
self.cache
.expanded_rows
.range(0..row_nr)
.map(|(expanded_row_nr, additional_lines)| {
self.egui_ctx.animate_value_with_time(
self.row_id(*expanded_row_nr),
*additional_lines as f32 * self.row_height,
self.egui_ctx.style().animation_time,
)
})
.sum::<f32>()
+ row_nr as f32 * self.row_height
}
/// Returns whether the first line of the specified row is odd.
///
/// This depends on how many additional lines the rows before have.
pub(crate) fn is_row_odd(&self, row_nr: u64) -> bool {
let total_lines = self
.cache
.expanded_rows
.range(0..row_nr)
.map(|(_, additional_lines)| *additional_lines)
.sum::<u64>()
+ row_nr;
total_lines % 2 == 1
}
/// Return by how many additional lines this row is expended.
pub(crate) fn additional_lines_for_row(&self, row_nr: u64) -> u64 {
self.cache.expanded_rows.get(&row_nr).copied().unwrap_or(0)
}
/// Set the expansion of a row.
///
/// Units are in extra row heights.
pub(crate) fn set_additional_lines_for_row(&mut self, row_nr: u64, additional_lines: u64) {
// Note: don't delete the entry when set to 0, this breaks animation.
// If this is the first time this row is expended, we must seed the corresponding animation
// cache.
if !self.cache.expanded_rows.contains_key(&row_nr) {
self.egui_ctx.animate_value_with_time(
self.row_id(row_nr),
0.0,
self.egui_ctx.style().animation_time,
);
}
self.cache.expanded_rows.insert(row_nr, additional_lines);
}
/// Collapse a row.
pub(crate) fn remove_additional_lines_for_row(&mut self, row_nr: u64) {
self.set_additional_lines_for_row(row_nr, 0);
}
#[inline]
fn row_id(&self, row_nr: u64) -> egui::Id {
self.id.with(row_nr)
}
}