Skip to main content

egui_table_kit/filter/
highlight.rs

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