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
//! Second-generation list item.
//!
//! TODO(ab): provide some top-level documentation here.

mod button_content;
mod item_button;
mod label_content;
#[allow(clippy::module_inception)]
mod list_item;
mod other_contents;
mod property_content;
mod scope;

pub use button_content::*;
pub use item_button::*;
pub use label_content::*;
pub use list_item::*;
pub use other_contents::*;
pub use property_content::*;
pub use scope::*;

/// Context provided to [`ListItemContent`] implementations
#[derive(Debug, Clone)]
pub struct ContentContext<'a> {
    /// Area available for the content to draw in.
    pub rect: egui::Rect,

    /// Background area
    ///
    /// This is the area covered by the full-span highlighting. Useful for testing if the cursor is
    /// over the item.
    pub bg_rect: egui::Rect,

    /// List item response.
    ///
    /// Note: this response reflects the hover state when [`ListItem::force_hovered`] is used,
    /// regardless of the actual mouse position.
    pub response: &'a egui::Response,

    /// The current list item.
    pub list_item: &'a ListItem,

    /// Layout information to use for rendering.
    pub layout_info: LayoutInfo,
}

#[derive(Debug, Clone, Copy)]
pub enum DesiredWidth {
    /// The content needs exactly this width for display.
    ///
    /// This mode is useful when it is important to not overallocate (e.g. streams view).
    Exact(f32),

    /// The content needs at least this width for display, but will use more if available.
    ///
    /// In this mode, [`ListItem`] will try to allocate this much width in addition to indent and
    /// collapsing triangle (if any). This may trigger some scrolling. In any case, the content will
    /// be provided with `ui.available_width()`.
    AtLeast(f32),
}

impl Default for DesiredWidth {
    fn default() -> Self {
        Self::AtLeast(0.0)
    }
}

pub trait ListItemContent {
    /// UI for everything that is after the indent and the collapsing triangle (if any).
    ///
    /// The content should render within the provided `context.rect`.
    ///
    /// If the content has some interactive elements, it should return its response. In particular,
    /// if the response is hovered, the list item will show a dimmer background highlight.
    //TODO(ab): could the return type be just a bool meaning "inner interactive widget was hovered"?
    fn ui(self: Box<Self>, ui: &mut egui::Ui, context: &ContentContext<'_>);

    /// The desired width of the content.
    fn desired_width(&self, _ui: &egui::Ui) -> DesiredWidth {
        DesiredWidth::AtLeast(0.0)
    }
}

/// A button that can be used by a list item.
///
/// The button is expected to have a size of [`crate::DesignTokens::small_icon_size`].
///
/// How/where the button is displayed is up to the [`crate::list_item::ListItemContent`]
/// implementation.
pub trait ItemButton {
    /// Draw the button.
    fn ui(self: Box<Self>, ui: &mut egui::Ui) -> egui::Response;
}