feat(color_picker): apply gradient shifting to hue slider

This also removes vertical line gap in the center of slider handle by adding current hue color to handle.
This commit is contained in:
Aadil 2026-06-24 01:43:35 +05:30 committed by GitHub
parent 76bc13ff40
commit 417923fbaf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -5,13 +5,11 @@
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::LazyLock;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use crate::Element;
use crate::theme::iced::Slider;
use crate::theme::{Button, THEME};
use crate::theme::{Button, THEME, Theme, iced::Slider};
use crate::widget::button::Catalog;
use crate::widget::segmented_button::Entity;
use crate::widget::{container, slider};
@ -39,37 +37,6 @@ use super::{Icon, button, segmented_control, text, text_input, tooltip};
#[doc(inline)]
pub use ColorPickerModel as Model;
// TODO is this going to look correct enough?
pub static HSV_RAINBOW: LazyLock<Vec<Color>> = LazyLock::new(|| {
(0u16..8)
.map(|h| {
Color::from(palette::Srgba::from_color(palette::Hsv::new_srgb_const(
RgbHue::new(f32::from(h) * 360.0 / 7.0),
1.0,
1.0,
)))
})
.collect()
});
fn hsv_rainbow(low_hue: f32, high_hue: f32) -> Vec<ColorStop> {
let mut colors = Vec::new();
let steps: u8 = 7;
let step_size = (high_hue - low_hue) / f32::from(steps);
for i in 0..=steps {
let hue = low_hue + step_size * f32::from(i);
colors.push(ColorStop {
color: Color::from(palette::Srgba::from_color(palette::Hsv::new_srgb_const(
RgbHue::new(hue),
1.0,
1.0,
))),
offset: f32::from(i) / f32::from(steps),
});
}
colors
}
const MAX_RECENT: usize = 20;
#[derive(Debug, Clone)]
@ -301,23 +268,67 @@ where
copy_to_clipboard_label: T,
copied_to_clipboard_label: T,
) -> ColorPicker<'a, Message> {
fn rail_backgrounds(hue: f32) -> (Background, Background) {
let low_range = hsv_rainbow(0., hue);
let high_range = hsv_rainbow(hue, 360.);
(
Background::Gradient(iced::Gradient::Linear(
Linear::new(Radians(90.0)).add_stops(low_range),
)),
Background::Gradient(iced::Gradient::Linear(
Linear::new(Radians(90.0)).add_stops(high_range),
)),
)
}
let on_update = self.on_update;
let spacing = THEME.lock().unwrap().cosmic().spacing;
let color_slider_style = Rc::new(move |t: &Theme| {
let cosmic = t.cosmic();
let mut a = slider::Catalog::style(t, &Slider::default(), slider::Status::Active);
a.rail.backgrounds = (
// active track
Background::Gradient(iced::Gradient::Linear(
Linear::new(Radians(90.0)).add_stops((0..8_u8).map(|index| {
let offset;
let hue = self.active_color.hue.into_positive_degrees();
let new_hue: f32;
if hue <= f32::from(index) * 360.0 / 7.0 {
offset = 1.0;
new_hue = hue;
} else {
offset = (f32::from(index) / 7.0) * (360.0 / hue);
new_hue = f32::from(index) * 360.0 / 7.0;
}
ColorStop {
color: Color::from(palette::Srgba::from_color(
palette::Hsv::new_srgb_const(RgbHue::new(new_hue), 1.0, 1.0),
)),
offset,
}
})),
)),
// inactive track
Background::Gradient(iced::Gradient::Linear(
Linear::new(Radians(90.0)).add_stops((0..8_u8).map(|index| {
let offset;
let hue = self.active_color.hue.into_positive_degrees();
let new_hue: f32;
if hue >= f32::from(index) * 360.0 / 7.0 {
offset = 0.0;
new_hue = hue;
} else {
offset =
((f32::from(index) / 7.0) - (hue / 360.0)) / (1.0 - (hue / 360.0));
new_hue = f32::from(index) * 360.0 / 7.0;
}
ColorStop {
color: Color::from(palette::Srgba::from_color(
palette::Hsv::new_srgb_const(RgbHue::new(new_hue), 1.0, 1.0),
)),
offset,
}
})),
)),
);
a.rail.width = 8.0;
a.handle.background = Background::Color(Color::from(palette::Srgba::from_color(
palette::Hsv::new_srgb_const(self.active_color.hue, 1.0, 1.0),
)));
a.handle.shape = HandleShape::Circle { radius: 8.0 };
a.handle.border_color = cosmic.palette.neutral_10.into();
a.handle.border_width = 4.0;
a
});
let mut inner = column![
// segmented buttons
segmented_control::horizontal(self.model)
@ -332,56 +343,22 @@ where
.width(self.width)
.height(self.height),
slider(
0.001..=359.99,
0.0..=359.99,
self.active_color.hue.into_positive_degrees(),
move |v| {
let mut new = self.active_color;
new.hue = v.into();
on_update(ColorPickerUpdate::ActiveColor(new))
}
},
)
.on_release(on_update(ColorPickerUpdate::ActionFinished))
.class(Slider::Custom {
active: Rc::new(move |t| {
let cosmic = t.cosmic();
let mut a =
slider::Catalog::style(t, &Slider::default(), slider::Status::Active);
let hue = self.active_color.hue.into_positive_degrees();
a.rail.backgrounds = rail_backgrounds(hue);
a.rail.width = 8.0;
a.handle.background = Color::TRANSPARENT.into();
a.handle.shape = HandleShape::Circle { radius: 8.0 };
a.handle.border_color = cosmic.palette.neutral_10.into();
a.handle.border_width = 4.0;
a
}),
hovered: Rc::new(move |t| {
let cosmic = t.cosmic();
let mut a =
slider::Catalog::style(t, &Slider::default(), slider::Status::Active);
let hue = self.active_color.hue.into_positive_degrees();
a.rail.backgrounds = rail_backgrounds(hue);
a.rail.width = 8.0;
a.handle.background = Color::TRANSPARENT.into();
a.handle.shape = HandleShape::Circle { radius: 8.0 };
a.handle.border_color = cosmic.palette.neutral_10.into();
a.handle.border_width = 4.0;
a
}),
dragging: Rc::new(move |t| {
let cosmic = t.cosmic();
let mut a =
slider::Catalog::style(t, &Slider::default(), slider::Status::Active);
let hue = self.active_color.hue.into_positive_degrees();
a.rail.backgrounds = rail_backgrounds(hue);
a.rail.width = 8.0;
a.handle.background = Color::TRANSPARENT.into();
a.handle.shape = HandleShape::Circle { radius: 8.0 };
a.handle.border_color = cosmic.palette.neutral_10.into();
a.handle.border_width = 4.0;
a
}),
active: color_slider_style.clone(),
hovered: color_slider_style.clone(),
dragging: color_slider_style,
})
.step(4.0 / 17.0)
.shift_step(64.0 / 17.0)
.width(self.width),
text_input("", self.input_color)
.on_input(move |s| on_update(ColorPickerUpdate::Input(s)))