egui_table_kit/filter/
highlight.rs1use std::sync::{Arc, LazyLock};
4
5use fluent_zero::t;
6use parking_lot::RwLock;
7
8use crate::error::TableError;
9
10pub static SELECT_COLOR: LazyLock<Arc<RwLock<u8>>> =
12 LazyLock::new(|| Arc::new(RwLock::new(Default::default())));
13
14pub fn select_color_meta(
16 index: u8,
17 org: &[[u8; 3]],
18 user: &[[u8; 3]],
19) -> Result<(usize, usize, [u8; 3]), TableError> {
20 let index = index as usize;
21 let org_color_len = org.len();
22
23 let color = if index < org_color_len {
24 Some(*org.get(index).ok_or(TableError::CorruptedState)?)
25 } else {
26 user.get(index - org_color_len).copied()
27 };
28 Ok((index, org_color_len, color.unwrap_or_else(Default::default)))
29}
30
31pub fn select_color(index: u8, org: &[[u8; 3]], user: &[[u8; 3]]) -> Result<[u8; 3], TableError> {
33 select_color_meta(index, org, user).map(|(_, _, color)| color)
34}
35
36pub fn color_select_button(
38 ui: &mut egui::Ui,
39 color: egui::Color32,
40 selected: bool,
41 skinny: bool,
42) -> egui::Response {
43 let size = ui.spacing().interact_size;
44 let (rect, response) = ui.allocate_exact_size(
45 egui::vec2(if skinny { size.x / 2.0 } else { size.x }, size.y),
46 egui::Sense::click(),
47 );
48 response.widget_info(|| egui::WidgetInfo::new(egui::WidgetType::ColorButton));
49
50 if ui.is_rect_visible(rect) {
51 let visuals = if selected {
52 let mut selected_visuals = ui.visuals().widgets.open;
53 selected_visuals.bg_stroke.color = egui::Color32::DARK_BLUE;
54 selected_visuals.bg_stroke.width = 2.0;
55 selected_visuals
56 } else {
57 *ui.style().interact(&response)
58 };
59
60 let rect = rect.expand(visuals.expansion);
61 egui::color_picker::show_color_at(ui.painter(), color, rect);
62
63 let rounding = visuals.corner_radius.at_most(2);
64 ui.painter()
65 .rect_stroke(rect, rounding, visuals.bg_stroke, egui::StrokeKind::Middle);
66 }
67
68 response
69}
70
71pub fn opt_color_select_button(
73 ui: &mut egui::Ui,
74 label: &str,
75 color: &mut Option<u8>,
76 selected: bool,
77 skinny: bool,
78 highlight_options_r1: &[[u8; 3]],
79 highlight_options_r2: &[[u8; 3]],
80) -> Result<(), TableError> {
81 ui.label(label);
82
83 match color {
84 Some(inner) => {
85 let [r, g, b] = select_color(*inner, highlight_options_r1, highlight_options_r2)?;
86 if color_select_button(ui, egui::Color32::from_rgb(r, g, b), selected, skinny).clicked()
87 {
88 *color = None;
89 }
90 }
91 None => {
92 if ui.button("➕").clicked() {
93 *color = Some(*SELECT_COLOR.read());
94 }
95 }
96 }
97
98 Ok(())
99}
100
101pub struct HighlightFilter<'a> {
103 label: &'a str,
104}
105
106impl<'a> HighlightFilter<'a> {
107 #[must_use]
109 pub const fn new(label: &'a str) -> Self {
110 Self { label }
111 }
112
113 pub fn ui(
115 self,
116 ui: &mut egui::Ui,
117 highlight_select: &mut Option<Option<u8>>,
118 highlight_options_r1: &[[u8; 3]],
119 highlight_options_r2: &[[u8; 3]],
120 ) -> Result<(), TableError> {
121 ui.horizontal(|ui| {
122 if let Some(highlight_filter) = highlight_select {
123 opt_color_select_button(
124 ui,
125 self.label,
126 highlight_filter,
127 false,
128 false,
129 highlight_options_r1,
130 highlight_options_r2,
131 )?;
132
133 if ui.button("❌").on_hover_text(t!("remove-filter")).clicked() {
134 *highlight_select = None;
135 }
136 } else if ui.button(self.label).clicked() {
137 *highlight_select = Some(None);
138 }
139
140 Ok(())
141 })
142 .inner
143 }
144}