egui_macview/controls/table_view/
header.rs1use crate::symbols::MacSymbol;
4use crate::theme::MacTheme;
5use egui::{Align, Align2, FontId, Id, Pos2, Rect, Sense, Stroke, Ui};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum MacSortDirection {
10 Ascending,
12 Descending,
14}
15
16impl MacSortDirection {
17 pub fn toggled(self) -> Self {
19 match self {
20 Self::Ascending => Self::Descending,
21 Self::Descending => Self::Ascending,
22 }
23 }
24}
25
26#[derive(Debug, Clone)]
28pub struct MacTableColumn<'a> {
29 pub title: &'a str,
31 pub width: f32,
33 pub alignment: Align,
35 pub sort_direction: Option<MacSortDirection>,
37}
38
39impl<'a> MacTableColumn<'a> {
40 pub fn new(title: &'a str, width: f32) -> Self {
42 Self {
43 title,
44 width,
45 alignment: Align::Min,
46 sort_direction: None,
47 }
48 }
49
50 pub fn align(mut self, alignment: Align) -> Self {
52 self.alignment = alignment;
53 self
54 }
55
56 pub fn sort(mut self, direction: Option<MacSortDirection>) -> Self {
58 self.sort_direction = direction;
59 self
60 }
61}
62
63pub(crate) fn render_table_header(
65 ui: &mut Ui,
66 table_id: Id,
67 theme: &MacTheme,
68 header_rect: Rect,
69 columns: &[MacTableColumn<'_>],
70 enabled: bool,
71) -> (Option<usize>, Option<(usize, MacSortDirection)>) {
72 let tm = theme.table_view();
73 let pad_h = tm.cell_padding_horizontal.points();
74 let font_sz = tm.header_font_size.points();
75 let chevron_sz = tm.sort_chevron_size.points();
76 let chevron_gap = tm.sort_chevron_spacing.points();
77 let sep_w = tm.separator_width.points();
78 let div_inset = tm.header_divider_inset.points();
79
80 let painter = ui.painter_at(header_rect);
81 painter.rect_filled(header_rect, 0, theme.control());
82
83 let mut cursor_x = header_rect.left();
84 let mut clicked_header = None;
85 let mut toggled_sort = None;
86
87 for (col_idx, col) in columns.iter().enumerate() {
88 let col_rect = Rect::from_min_max(
89 Pos2::new(cursor_x, header_rect.top()),
90 Pos2::new(
91 (cursor_x + col.width).min(header_rect.right()),
92 header_rect.bottom(),
93 ),
94 );
95 let hdr_id = table_id.with(("hdr_col", col_idx));
96 let sense = if enabled {
97 Sense::click()
98 } else {
99 Sense::hover()
100 };
101 let resp = ui.interact(col_rect, hdr_id, sense);
102
103 if resp.hovered() && enabled {
104 painter.rect_filled(col_rect, 0, theme.control_background());
105 }
106
107 if resp.clicked() && enabled {
108 clicked_header = Some(col_idx);
109 let next_dir = col
110 .sort_direction
111 .map(MacSortDirection::toggled)
112 .unwrap_or(MacSortDirection::Ascending);
113 toggled_sort = Some((col_idx, next_dir));
114 }
115
116 let right_reserve = if col.sort_direction.is_some() {
117 chevron_sz + pad_h
118 } else {
119 pad_h
120 };
121 let title_clip_rect = Rect::from_min_max(
122 col_rect.min,
123 Pos2::new(
124 (col_rect.right() - right_reserve).max(col_rect.left()),
125 col_rect.max.y,
126 ),
127 );
128 let title_pos = Pos2::new(col_rect.left() + pad_h, col_rect.center().y);
129 painter.with_clip_rect(title_clip_rect).text(
130 title_pos,
131 Align2::LEFT_CENTER,
132 col.title,
133 FontId::proportional(font_sz),
134 theme.label(),
135 );
136
137 if let Some(dir) = col.sort_direction {
138 let symbol = match dir {
139 MacSortDirection::Ascending => MacSymbol::ChevronUp,
140 MacSortDirection::Descending => MacSymbol::ChevronDown,
141 };
142 let chev_x = (col_rect.right() - pad_h - chevron_gap).max(col_rect.left() + pad_h);
143 painter.text(
144 Pos2::new(chev_x, col_rect.center().y),
145 Align2::RIGHT_CENTER,
146 symbol.as_str(),
147 FontId::proportional(chevron_sz),
148 theme.secondary_label(),
149 );
150 }
151
152 cursor_x += col.width;
153 if col_idx + 1 < columns.len() && cursor_x < header_rect.right() {
154 painter.line_segment(
155 [
156 Pos2::new(cursor_x, header_rect.top() + div_inset),
157 Pos2::new(cursor_x, header_rect.bottom() - div_inset),
158 ],
159 Stroke::new(sep_w, theme.separator()),
160 );
161 }
162 }
163
164 painter.line_segment(
165 [
166 Pos2::new(header_rect.left(), header_rect.bottom()),
167 Pos2::new(header_rect.right(), header_rect.bottom()),
168 ],
169 Stroke::new(sep_w, theme.separator()),
170 );
171
172 (clicked_header, toggled_sort)
173}