Skip to content

Commit

Permalink
color_picker rearrange and input type add
Browse files Browse the repository at this point in the history
  • Loading branch information
IVAN-MK7 committed Oct 8, 2023
1 parent 9f399a4 commit ef89897
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 76 deletions.
26 changes: 26 additions & 0 deletions crates/egui/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,9 @@ pub struct Visuals {

/// Show a spinner when loading an image.
pub image_loading_spinners: bool,

/// Sets the color picker's input values type.
pub color_picker_input_values_type: ColorPickerInputType,
}

impl Visuals {
Expand Down Expand Up @@ -867,6 +870,8 @@ impl Visuals {
interact_cursor: None,

image_loading_spinners: true,

color_picker_input_values_type: ColorPickerInputType::U8,
}
}

Expand Down Expand Up @@ -1443,6 +1448,8 @@ impl Visuals {
interact_cursor,

image_loading_spinners,

color_picker_input_values_type: _,
} = self;

ui.collapsing("Background Colors", |ui| {
Expand Down Expand Up @@ -1521,6 +1528,9 @@ impl Visuals {
ui.checkbox(image_loading_spinners, "Image loading spinners")
.on_hover_text("Show a spinner when an Image is loading");

ui.checkbox(image_loading_spinners, "Image loading spinners")
.on_hover_text("Show a spinner when an Image is loading");

ui.vertical_centered(|ui| reset_button(ui, self));
}
}
Expand Down Expand Up @@ -1630,3 +1640,19 @@ fn rounding_ui(ui: &mut Ui, rounding: &mut Rounding) {
}
});
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum ColorPickerInputType {
U8,
F32,
}

impl std::fmt::Display for ColorPickerInputType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ColorPickerInputType::U8 => write!(f, "U8"),
ColorPickerInputType::F32 => write!(f, "F32"),
}
}
}
192 changes: 116 additions & 76 deletions crates/egui/src/widgets/color_picker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,17 +228,47 @@ pub enum Alpha {
}

fn color_text_ui(ui: &mut Ui, color: impl Into<Color32>, alpha: Alpha) {
use crate::style::ColorPickerInputType;
let color = color.into();
ui.horizontal(|ui| {
let [r, g, b, a] = color.to_array();
let [r, g, b, a] = match ui.style().visuals.color_picker_input_values_type {
ColorPickerInputType::U8 => color.to_array().map(|c| c.to_string()),
ColorPickerInputType::F32 => {
let rgba_premultiplied =
(std::convert::Into::<Hsva>::into(color)).to_rgba_premultiplied();
rgba_premultiplied.map(|c| c.to_string().char_range(0..5).to_owned())
}
};

if ui.button("📋").on_hover_text("Click to copy").clicked() {
if alpha == Alpha::Opaque {
ui.ctx().copy_text(format!("{r}, {g}, {b}"));
} else {
ui.ctx().copy_text(format!("{r}, {g}, {b}, {a}"));
ui.horizontal(|ui| {
if ui
.button(
ui.style()
.visuals
.color_picker_input_values_type
.to_string(),
)
.clicked()
{
let mut visuals = ui.style().visuals.clone();
match ui.style().visuals.color_picker_input_values_type {
ColorPickerInputType::U8 => {
visuals.color_picker_input_values_type = ColorPickerInputType::F32;
}
ColorPickerInputType::F32 => {
visuals.color_picker_input_values_type = ColorPickerInputType::U8;
}
}
ui.ctx().set_visuals(visuals);
}
}
if ui.button("📋").on_hover_text("Click to copy").clicked() {
if alpha == Alpha::Opaque {
ui.ctx().copy_text(format!("{r}, {g}, {b}"));
} else {
ui.ctx().copy_text(format!("{r}, {g}, {b}, {a}"));
}
}
});

if alpha == Alpha::Opaque {
ui.label(format!("rgb({r}, {g}, {b})"))
Expand All @@ -251,58 +281,67 @@ fn color_text_ui(ui: &mut Ui, color: impl Into<Color32>, alpha: Alpha) {
}

fn color_picker_hsvag_2d(ui: &mut Ui, hsvag: &mut HsvaGamma, alpha: Alpha) {
use crate::style::ColorPickerInputType;

// Send an Opaque Alpha also when Alpha is BlendOrAdditive with negative alpha value (signals Additive blending),
// so to hide the alpha's DragValue in both cases.
let alpha_control = if hsvag.a < 0.0 { Alpha::Opaque } else { alpha };

let mut srgba_unmultiplied = (std::convert::Into::<Hsva>::into(*hsvag)).to_srgba_unmultiplied();
// Update hsvag only if the converted srgba is changed, this is because hsvag is made of f32,
// and the conversion between u8 and f32 loses a bit of the color precision, causing little flickering on hsvag based ui widgets.
if srgba_edit_ui(ui, &mut srgba_unmultiplied, alpha_control) {
// Additive blending, signaled by the negative Alpha.
if hsvag.a < 0.0 {
let stored_a = hsvag.a;
// Alpha to 0 instead of negative, so it won't pop back to Normal blending when RGB are modified.
srgba_unmultiplied[3] = 0;
// This conversion above sets Alpha to 0 in case it was negative, stored_a is used to back-up that value.
*hsvag = HsvaGamma::from(Hsva::from_srgba_unmultiplied(srgba_unmultiplied));
// stored_a keeps the Alpha value that was set during Normal blending so that in case we alter RGB in Additive blending (negative Alpha)
// and then switch back to Normal blending it gets that Alpha value back.
hsvag.a = stored_a;
}
// Normal blending.
else {
*hsvag = HsvaGamma::from(Hsva::from_srgba_unmultiplied(srgba_unmultiplied));
}
}

let mut rgba_unmultiplied = (std::convert::Into::<Hsva>::into(*hsvag)).to_rgba_unmultiplied();
// Update hsvag only if the converted srgba is changed, this is because hsvag is made of f32,
// and the conversion between u8 and f32 loses a bit of the color precision, causing little flickering on hsvag based ui widgets.
if rgba_edit_ui(ui, &mut rgba_unmultiplied, alpha_control) {
// Additive blending, signaled by the negative Alpha.
if hsvag.a < 0.0 {
let stored_a = hsvag.a;
// Alpha to 0 instead of negative, so it won't pop back to Normal blending when RGB are modified.
rgba_unmultiplied[3] = 0.0;
// This conversion above sets Alpha to 0 in case it was negative, stored_a is used to back-up that value.
*hsvag = HsvaGamma::from(Hsva::from_rgb([
rgba_unmultiplied[0],
rgba_unmultiplied[1],
rgba_unmultiplied[2],
]));
// stored_a keeps the Alpha value that was set during Normal blending so that in case we alter RGB in Additive blending (negative Alpha)
// and then switch back to Normal blending it gets that Alpha value back.
hsvag.a = stored_a;
match ui.style().visuals.color_picker_input_values_type {
ColorPickerInputType::U8 => {
let mut srgba_unmultiplied =
(std::convert::Into::<Hsva>::into(*hsvag)).to_srgba_unmultiplied();
// Update hsvag only if the converted srgba is changed, this is because hsvag is made of f32,
// and the conversion between u8 and f32 loses a bit of the color precision, causing little flickering on hsvag based ui widgets.
if srgba_edit_ui(ui, &mut srgba_unmultiplied, alpha_control) {
// Additive blending, signaled by the negative Alpha.
if hsvag.a < 0.0 {
let stored_a = hsvag.a;
// Alpha to 0 instead of negative, so it won't pop back to Normal blending when RGB are modified.
srgba_unmultiplied[3] = 0;
// This conversion above sets Alpha to 0 in case it was negative, stored_a is used to back-up that value.
*hsvag = HsvaGamma::from(Hsva::from_srgba_unmultiplied(srgba_unmultiplied));
// stored_a keeps the Alpha value that was set during Normal blending so that in case we alter RGB in Additive blending (negative Alpha)
// and then switch back to Normal blending it gets that Alpha value back.
hsvag.a = stored_a;
}
// Normal blending.
else {
*hsvag = HsvaGamma::from(Hsva::from_srgba_unmultiplied(srgba_unmultiplied));
}
}
}
// Normal blending.
else {
*hsvag = HsvaGamma::from(Hsva::from_rgba_unmultiplied(
rgba_unmultiplied[0],
rgba_unmultiplied[1],
rgba_unmultiplied[2],
rgba_unmultiplied[3],
));
ColorPickerInputType::F32 => {
let mut rgba_unmultiplied =
(std::convert::Into::<Hsva>::into(*hsvag)).to_rgba_unmultiplied();
// Update hsvag only if the converted srgba is changed, this is because hsvag is made of f32,
// and the conversion between u8 and f32 loses a bit of the color precision, causing little flickering on hsvag based ui widgets.
if rgba_edit_ui(ui, &mut rgba_unmultiplied, alpha_control) {
// Additive blending, signaled by the negative Alpha.
if hsvag.a < 0.0 {
let stored_a = hsvag.a;
// Alpha to 0 instead of negative, so it won't pop back to Normal blending when RGB are modified.
rgba_unmultiplied[3] = 0.0;
// This conversion above sets Alpha to 0 in case it was negative, stored_a is used to back-up that value.
*hsvag = HsvaGamma::from(Hsva::from_rgb([
rgba_unmultiplied[0],
rgba_unmultiplied[1],
rgba_unmultiplied[2],
]));
// stored_a keeps the Alpha value that was set during Normal blending so that in case we alter RGB in Additive blending (negative Alpha)
// and then switch back to Normal blending it gets that Alpha value back.
hsvag.a = stored_a;
}
// Normal blending.
else {
*hsvag = HsvaGamma::from(Hsva::from_rgba_unmultiplied(
rgba_unmultiplied[0],
rgba_unmultiplied[1],
rgba_unmultiplied[2],
rgba_unmultiplied[3],
));
}
}
}
}

Expand All @@ -329,26 +368,20 @@ fn color_picker_hsvag_2d(ui: &mut Ui, hsvag: &mut HsvaGamma, alpha: Alpha) {
}
});
}
let additive = hsvag.a < 0.0;

let opaque = HsvaGamma { a: 1.0, ..*hsvag };

if alpha == Alpha::Opaque {
hsvag.a = 1.0;
} else {
let a = &mut hsvag.a;
let HsvaGamma { h, s, v, a: _ } = hsvag;

if alpha == Alpha::OnlyBlend {
if *a < 0.0 {
*a = 0.5; // was additive, but isn't allowed to be
}
color_slider_1d(ui, a, |a| HsvaGamma { a, ..opaque }.into()).on_hover_text("Alpha");
} else if !additive {
color_slider_1d(ui, a, |a| HsvaGamma { a, ..opaque }.into()).on_hover_text("Alpha");
}
if false {
color_slider_1d(ui, s, |s| HsvaGamma { s, ..opaque }.into()).on_hover_text("Saturation");
}

let HsvaGamma { h, s, v, a: _ } = hsvag;
if false {
color_slider_1d(ui, v, |v| HsvaGamma { v, ..opaque }.into()).on_hover_text("Value");
}

color_slider_2d(ui, s, v, |s, v| HsvaGamma { s, v, ..opaque }.into());

color_slider_1d(ui, h, |h| {
HsvaGamma {
Expand All @@ -361,15 +394,22 @@ fn color_picker_hsvag_2d(ui: &mut Ui, hsvag: &mut HsvaGamma, alpha: Alpha) {
})
.on_hover_text("Hue");

if false {
color_slider_1d(ui, s, |s| HsvaGamma { s, ..opaque }.into()).on_hover_text("Saturation");
}
let additive = hsvag.a < 0.0;

if false {
color_slider_1d(ui, v, |v| HsvaGamma { v, ..opaque }.into()).on_hover_text("Value");
}
if alpha == Alpha::Opaque {
hsvag.a = 1.0;
} else {
let a = &mut hsvag.a;

color_slider_2d(ui, s, v, |s, v| HsvaGamma { s, v, ..opaque }.into());
if alpha == Alpha::OnlyBlend {
if *a < 0.0 {
*a = 0.5; // was additive, but isn't allowed to be
}
color_slider_1d(ui, a, |a| HsvaGamma { a, ..opaque }.into()).on_hover_text("Alpha");
} else if !additive {
color_slider_1d(ui, a, |a| HsvaGamma { a, ..opaque }.into()).on_hover_text("Alpha");
}
}
}

/// Shows 4 `DragValue` widgets to be used to edit the RGBA u8 values.
Expand Down Expand Up @@ -480,7 +520,7 @@ pub fn color_edit_button_hsva(ui: &mut Ui, hsva: &mut Hsva, alpha: Alpha) -> Res
ui.memory_mut(|mem| mem.toggle_popup(popup_id));
}

const COLOR_SLIDER_WIDTH: f32 = 210.0;
const COLOR_SLIDER_WIDTH: f32 = 240.0;

// TODO(emilk): make it easier to show a temporary popup that closes when you click outside it
if ui.memory(|mem| mem.is_popup_open(popup_id)) {
Expand Down

0 comments on commit ef89897

Please sign in to comment.