use egui::{
Color32, Popup, PopupCloseBehavior, Response, Ui, Vec2, Widget,
color_picker::{Alpha, color_picker_color32},
};
use re_sdk_types::datatypes::Rgba32;
use re_ui::UiExt as _;
use re_viewer_context::MaybeMutRef;
pub struct ColorSwatch<'a> {
color: &'a mut MaybeMutRef<'a, Rgba32>,
}
impl<'a> ColorSwatch<'a> {
pub fn new(color: &'a mut MaybeMutRef<'a, Rgba32>) -> Self {
Self { color }
}
}
impl Widget for ColorSwatch<'_> {
fn ui(self, ui: &mut Ui) -> Response {
let [r, g, b, a] = self.color.to_array();
let size = Vec2::splat(ui.tokens().color_swatch_size);
let (rect, response) = ui.allocate_exact_size(size, egui::Sense::click());
if ui.is_rect_visible(rect) {
let stroke = if response.hovered() && self.color.as_mut().is_some() {
ui.tokens().color_swatch_interactive_stroke
} else {
ui.tokens().color_swatch_noninteractive_stroke
};
ui.painter().rect(
rect,
3.0,
#[expect(clippy::disallowed_methods)] Color32::from_rgb(r, g, b),
stroke,
egui::StrokeKind::Inside,
);
}
let mut response = response.on_hover_ui(|ui| {
ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
ui.monospace(format!("#{r:02x}{g:02x}{b:02x}{a:02x}"));
});
if let Some(target_color) = self.color.as_mut() {
let popup_id = ui.auto_id_with("popup");
const COLOR_SLIDER_WIDTH: f32 = 275.0;
let mut color_changed = false;
Popup::menu(&response)
.id(popup_id)
.close_behavior(PopupCloseBehavior::CloseOnClickOutside)
.show(|ui| {
ui.spacing_mut().slider_width = COLOR_SLIDER_WIDTH;
#[expect(clippy::disallowed_methods)] let mut egui_color = Color32::from_rgba_unmultiplied(r, g, b, a);
if color_picker_color32(ui, &mut egui_color, Alpha::OnlyBlend) {
*target_color = Rgba32::from(egui_color);
color_changed = true;
}
});
if color_changed {
response.mark_changed();
}
}
response
}
}