slider: Add vertical and reverse support to Slider. (#556)
- Fix to support stop at any position. - Fix click bounds.
This commit is contained in:
parent
06e9b1a25c
commit
355dfa5556
4 changed files with 257 additions and 69 deletions
|
|
@ -1,16 +1,18 @@
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, IntoElement, ParentElement, Render, Styled, View, ViewContext, VisualContext,
|
div, hsla, px, Hsla, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
|
||||||
WindowContext,
|
View, ViewContext, VisualContext, WindowContext,
|
||||||
};
|
};
|
||||||
use ui::{
|
use ui::{
|
||||||
button::Button,
|
button::Button,
|
||||||
|
clipboard::Clipboard,
|
||||||
divider::Divider,
|
divider::Divider,
|
||||||
h_flex,
|
h_flex,
|
||||||
indicator::Indicator,
|
indicator::Indicator,
|
||||||
progress::Progress,
|
progress::Progress,
|
||||||
skeleton::Skeleton,
|
skeleton::Skeleton,
|
||||||
slider::{Slider, SliderEvent},
|
slider::{Slider, SliderEvent},
|
||||||
v_flex, IconName, Sizable,
|
theme::Colorize,
|
||||||
|
v_flex, ColorExt as _, ContextModal, IconName, Sizable,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ProgressStory {
|
pub struct ProgressStory {
|
||||||
|
|
@ -20,6 +22,9 @@ pub struct ProgressStory {
|
||||||
slider1_value: f32,
|
slider1_value: f32,
|
||||||
slider2: View<Slider>,
|
slider2: View<Slider>,
|
||||||
slider2_value: f32,
|
slider2_value: f32,
|
||||||
|
slider_hsl: [View<Slider>; 4],
|
||||||
|
slider_hsl_value: Hsla,
|
||||||
|
_subscritions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Story for ProgressStory {
|
impl super::Story for ProgressStory {
|
||||||
|
|
@ -45,30 +50,87 @@ impl ProgressStory {
|
||||||
.default_value(15.)
|
.default_value(15.)
|
||||||
.step(15.)
|
.step(15.)
|
||||||
});
|
});
|
||||||
cx.subscribe(&slider1, |this, _, event: &SliderEvent, cx| match event {
|
|
||||||
SliderEvent::Change(value) => {
|
|
||||||
this.slider1_value = *value;
|
|
||||||
cx.notify();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
let slider2 = cx.new_view(|_| Slider::horizontal().min(0.).max(5.).step(1.0));
|
let slider2 = cx.new_view(|_| Slider::horizontal().min(0.).max(5.).step(1.0));
|
||||||
cx.subscribe(&slider2, |this, _, event: &SliderEvent, cx| match event {
|
let slider_hsl = [
|
||||||
SliderEvent::Change(value) => {
|
cx.new_view(|_| {
|
||||||
this.slider2_value = *value;
|
Slider::vertical()
|
||||||
cx.notify();
|
.reverse()
|
||||||
}
|
.min(0.)
|
||||||
})
|
.max(1.)
|
||||||
.detach();
|
.step(0.01)
|
||||||
|
.default_value(0.)
|
||||||
|
}),
|
||||||
|
cx.new_view(|_| {
|
||||||
|
Slider::vertical()
|
||||||
|
.reverse()
|
||||||
|
.min(0.)
|
||||||
|
.max(1.)
|
||||||
|
.step(0.01)
|
||||||
|
.default_value(0.5)
|
||||||
|
}),
|
||||||
|
cx.new_view(|_| {
|
||||||
|
Slider::vertical()
|
||||||
|
.reverse()
|
||||||
|
.min(0.)
|
||||||
|
.max(1.)
|
||||||
|
.step(0.01)
|
||||||
|
.default_value(0.5)
|
||||||
|
}),
|
||||||
|
cx.new_view(|_| {
|
||||||
|
Slider::vertical()
|
||||||
|
.reverse()
|
||||||
|
.min(0.)
|
||||||
|
.max(1.)
|
||||||
|
.step(0.01)
|
||||||
|
.default_value(1.)
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut _subscritions = vec![
|
||||||
|
cx.subscribe(&slider1, |this, _, event: &SliderEvent, cx| match event {
|
||||||
|
SliderEvent::Change(value) => {
|
||||||
|
this.slider1_value = *value;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
cx.subscribe(&slider2, |this, _, event: &SliderEvent, cx| match event {
|
||||||
|
SliderEvent::Change(value) => {
|
||||||
|
this.slider2_value = *value;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
_subscritions.extend(
|
||||||
|
slider_hsl
|
||||||
|
.iter()
|
||||||
|
.map(|slider| {
|
||||||
|
cx.subscribe(slider, |this, _, event: &SliderEvent, cx| match event {
|
||||||
|
SliderEvent::Change(_) => {
|
||||||
|
this.slider_hsl_value = hsla(
|
||||||
|
this.slider_hsl[0].read(cx).value(),
|
||||||
|
this.slider_hsl[1].read(cx).value(),
|
||||||
|
this.slider_hsl[2].read(cx).value(),
|
||||||
|
this.slider_hsl[3].read(cx).value(),
|
||||||
|
);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
value: 50.,
|
value: 50.,
|
||||||
slider1_value: 15.,
|
slider1_value: 0.,
|
||||||
slider2_value: 1.,
|
slider2_value: 0.,
|
||||||
slider1,
|
slider1,
|
||||||
slider2,
|
slider2,
|
||||||
|
slider_hsl,
|
||||||
|
slider_hsl_value: gpui::red(),
|
||||||
|
_subscritions,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,6 +147,8 @@ impl gpui::FocusableView for ProgressStory {
|
||||||
|
|
||||||
impl Render for ProgressStory {
|
impl Render for ProgressStory {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
|
let rgb = SharedString::from(self.slider_hsl_value.to_hex_string());
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
.items_center()
|
.items_center()
|
||||||
.gap_y_3()
|
.gap_y_3()
|
||||||
|
|
@ -160,6 +224,65 @@ impl Render for ProgressStory {
|
||||||
.child(self.slider2.clone())
|
.child(self.slider2.clone())
|
||||||
.child(format!("Slider 2: {}", self.slider2_value)),
|
.child(format!("Slider 2: {}", self.slider2_value)),
|
||||||
)
|
)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_3()
|
||||||
|
.justify_start()
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.w_32()
|
||||||
|
.h_40()
|
||||||
|
.gap_3()
|
||||||
|
.items_center()
|
||||||
|
.child(self.slider_hsl[0].clone())
|
||||||
|
.child(format!("H: {:.0}", self.slider_hsl_value.h * 360.)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.w_32()
|
||||||
|
.h_40()
|
||||||
|
.gap_3()
|
||||||
|
.items_center()
|
||||||
|
.child(self.slider_hsl[1].clone())
|
||||||
|
.child(format!("S: {:.0}", self.slider_hsl_value.s * 100.)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.w_32()
|
||||||
|
.h_40()
|
||||||
|
.gap_3()
|
||||||
|
.items_center()
|
||||||
|
.child(self.slider_hsl[2].clone())
|
||||||
|
.child(format!("L: {:.0}", self.slider_hsl_value.l * 100.)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.w_32()
|
||||||
|
.h_40()
|
||||||
|
.gap_3()
|
||||||
|
.items_center()
|
||||||
|
.child(self.slider_hsl[3].clone())
|
||||||
|
.child(format!("A: {:.0}", self.slider_hsl_value.a * 100.)),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_2()
|
||||||
|
.items_center()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.w_32()
|
||||||
|
.p_1()
|
||||||
|
.rounded_lg()
|
||||||
|
.justify_center()
|
||||||
|
.bg(self.slider_hsl_value)
|
||||||
|
.child(rgb.clone())
|
||||||
|
.text_color(self.slider_hsl_value.invert()),
|
||||||
|
)
|
||||||
|
.child(Clipboard::new("copy-hsl").value(rgb).on_copied(|_, cx| {
|
||||||
|
cx.push_notification("Color copied to clipboard.")
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
)
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.mt_5()
|
.mt_5()
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,11 @@ use serde::{de::Error, Deserialize, Deserializer};
|
||||||
use crate::theme::hsl;
|
use crate::theme::hsl;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
pub(crate) trait ColorExt {
|
/// Extension methods for Hsla.
|
||||||
|
pub trait ColorExt {
|
||||||
|
/// Convert the color to a hex string. For example, "#F8FAFC".
|
||||||
fn to_hex_string(&self) -> String;
|
fn to_hex_string(&self) -> String;
|
||||||
|
/// Parse a hex string to a color.
|
||||||
fn parse_hex_string(hex: &str) -> Result<Hsla>;
|
fn parse_hex_string(hex: &str) -> Result<Hsla>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{theme::ActiveTheme, tooltip::Tooltip};
|
use crate::{h_flex, theme::ActiveTheme, tooltip::Tooltip, AxisExt};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
canvas, div, prelude::FluentBuilder as _, px, relative, Axis, Bounds, DragMoveEvent, EntityId,
|
canvas, div, prelude::FluentBuilder as _, px, Axis, Bounds, DragMoveEvent, EntityId,
|
||||||
EventEmitter, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, ParentElement as _,
|
EventEmitter, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, ParentElement as _,
|
||||||
Pixels, Point, Render, StatefulInteractiveElement as _, Styled, ViewContext,
|
Pixels, Point, Render, StatefulInteractiveElement as _, Styled, ViewContext,
|
||||||
VisualContext as _,
|
VisualContext as _,
|
||||||
|
|
@ -20,6 +20,8 @@ pub struct Slider {
|
||||||
max: f32,
|
max: f32,
|
||||||
step: f32,
|
step: f32,
|
||||||
value: f32,
|
value: f32,
|
||||||
|
reverse: bool,
|
||||||
|
percentage: f32,
|
||||||
bounds: Bounds<Pixels>,
|
bounds: Bounds<Pixels>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,23 +33,39 @@ impl Slider {
|
||||||
max: 100.0,
|
max: 100.0,
|
||||||
step: 1.0,
|
step: 1.0,
|
||||||
value: 0.0,
|
value: 0.0,
|
||||||
|
percentage: 0.0,
|
||||||
|
reverse: false,
|
||||||
bounds: Bounds::default(),
|
bounds: Bounds::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a horizontal slider.
|
||||||
pub fn horizontal() -> Self {
|
pub fn horizontal() -> Self {
|
||||||
Self::new(Axis::Horizontal)
|
Self::new(Axis::Horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a vertical slider.
|
||||||
|
pub fn vertical() -> Self {
|
||||||
|
Self::new(Axis::Vertical)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the reverse direction of the slider, default: false
|
||||||
|
pub fn reverse(mut self) -> Self {
|
||||||
|
self.reverse = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the minimum value of the slider, default: 0.0
|
/// Set the minimum value of the slider, default: 0.0
|
||||||
pub fn min(mut self, min: f32) -> Self {
|
pub fn min(mut self, min: f32) -> Self {
|
||||||
self.min = min;
|
self.min = min;
|
||||||
|
self.update_thumb_pos();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the maximum value of the slider, default: 100.0
|
/// Set the maximum value of the slider, default: 100.0
|
||||||
pub fn max(mut self, max: f32) -> Self {
|
pub fn max(mut self, max: f32) -> Self {
|
||||||
self.max = max;
|
self.max = max;
|
||||||
|
self.update_thumb_pos();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,27 +78,24 @@ impl Slider {
|
||||||
/// Set the default value of the slider, default: 0.0
|
/// Set the default value of the slider, default: 0.0
|
||||||
pub fn default_value(mut self, value: f32) -> Self {
|
pub fn default_value(mut self, value: f32) -> Self {
|
||||||
self.value = value;
|
self.value = value;
|
||||||
|
self.update_thumb_pos();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the value of the slider.
|
/// Set the value of the slider.
|
||||||
pub fn set_value(&mut self, value: f32, cx: &mut gpui::ViewContext<Self>) {
|
pub fn set_value(&mut self, value: f32, cx: &mut gpui::ViewContext<Self>) {
|
||||||
self.value = value;
|
self.value = value;
|
||||||
|
self.update_thumb_pos();
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return percentage value of the slider, range of 0.0..1.0
|
fn update_thumb_pos(&mut self) {
|
||||||
fn relative_value(&self) -> f32 {
|
self.percentage = self.value.clamp(self.min, self.max) / self.max;
|
||||||
let step = self.step;
|
}
|
||||||
let value = self.value;
|
|
||||||
let min = self.min;
|
|
||||||
let max = self.max;
|
|
||||||
|
|
||||||
let relative_value = (value - min) / (max - min);
|
/// Get the value of the slider.
|
||||||
let relative_step = step / (max - min);
|
pub fn value(&self) -> f32 {
|
||||||
|
self.value
|
||||||
let relative_value = (relative_value / relative_step).round() * relative_step;
|
|
||||||
relative_value.clamp(0.0, 1.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update value by mouse position
|
/// Update value by mouse position
|
||||||
|
|
@ -95,25 +110,45 @@ impl Slider {
|
||||||
let max = self.max;
|
let max = self.max;
|
||||||
let step = self.step;
|
let step = self.step;
|
||||||
|
|
||||||
let value = match axis {
|
let percentage = match axis {
|
||||||
Axis::Horizontal => {
|
Axis::Horizontal => {
|
||||||
let relative = (position.x - bounds.left()) / bounds.size.width;
|
if self.reverse {
|
||||||
min + (max - min) * relative
|
1. - (position.x - bounds.left()).clamp(px(0.), bounds.size.width)
|
||||||
|
/ bounds.size.width
|
||||||
|
} else {
|
||||||
|
(position.x - bounds.left()).clamp(px(0.), bounds.size.width)
|
||||||
|
/ bounds.size.width
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Axis::Vertical => {
|
Axis::Vertical => {
|
||||||
let relative = (position.y - bounds.top()) / bounds.size.height;
|
if self.reverse {
|
||||||
max - (max - min) * relative
|
1. - (position.y - bounds.top()).clamp(px(0.), bounds.size.height)
|
||||||
|
/ bounds.size.height
|
||||||
|
} else {
|
||||||
|
(position.y - bounds.top()).clamp(px(0.), bounds.size.height)
|
||||||
|
/ bounds.size.height
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let value = match axis {
|
||||||
|
Axis::Horizontal => min + (max - min) * percentage,
|
||||||
|
Axis::Vertical => max - (max - min) * percentage,
|
||||||
|
};
|
||||||
|
|
||||||
let value = (value / step).round() * step;
|
let value = (value / step).round() * step;
|
||||||
|
|
||||||
|
self.percentage = percentage;
|
||||||
self.value = value.clamp(self.min, self.max);
|
self.value = value.clamp(self.min, self.max);
|
||||||
cx.emit(SliderEvent::Change(self.value));
|
cx.emit(SliderEvent::Change(self.value));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_thumb(&self, cx: &mut ViewContext<Self>) -> impl gpui::IntoElement {
|
fn render_thumb(
|
||||||
|
&self,
|
||||||
|
thumb_bar_size: Pixels,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> impl gpui::IntoElement {
|
||||||
let value = self.value;
|
let value = self.value;
|
||||||
let entity_id = cx.entity_id();
|
let entity_id = cx.entity_id();
|
||||||
|
|
||||||
|
|
@ -136,9 +171,22 @@ impl Slider {
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.absolute()
|
.absolute()
|
||||||
.top(px(-5.))
|
.map(|this| match self.reverse {
|
||||||
.left(relative(self.relative_value()))
|
true => this
|
||||||
.ml(-px(8.))
|
.when(self.axis.is_horizontal(), |this| {
|
||||||
|
this.bottom(px(-5.)).right(thumb_bar_size).mr(-px(8.))
|
||||||
|
})
|
||||||
|
.when(self.axis.is_vertical(), |this| {
|
||||||
|
this.bottom(thumb_bar_size).right(px(-5.)).mb(-px(8.))
|
||||||
|
}),
|
||||||
|
false => this
|
||||||
|
.when(self.axis.is_horizontal(), |this| {
|
||||||
|
this.top(px(-5.)).left(thumb_bar_size).ml(-px(8.))
|
||||||
|
})
|
||||||
|
.when(self.axis.is_vertical(), |this| {
|
||||||
|
this.top(thumb_bar_size).left(px(-5.)).mt(-px(8.))
|
||||||
|
}),
|
||||||
|
})
|
||||||
.size_4()
|
.size_4()
|
||||||
.rounded_full()
|
.rounded_full()
|
||||||
.border_1()
|
.border_1()
|
||||||
|
|
@ -157,31 +205,45 @@ impl EventEmitter<SliderEvent> for Slider {}
|
||||||
|
|
||||||
impl Render for Slider {
|
impl Render for Slider {
|
||||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||||
div()
|
let thumb_bar_size = match self.axis {
|
||||||
|
Axis::Horizontal => self.percentage * self.bounds.size.width,
|
||||||
|
Axis::Vertical => self.percentage * self.bounds.size.height,
|
||||||
|
};
|
||||||
|
|
||||||
|
h_flex()
|
||||||
.id("slider")
|
.id("slider")
|
||||||
.on_mouse_down(MouseButton::Left, cx.listener(Self::on_mouse_down))
|
.on_mouse_down(MouseButton::Left, cx.listener(Self::on_mouse_down))
|
||||||
.h_5()
|
.when(self.axis.is_horizontal(), |this| {
|
||||||
|
this.items_center().h_6().w_full()
|
||||||
|
})
|
||||||
|
.when(self.axis.is_vertical(), |this| {
|
||||||
|
this.justify_center().w_6().h_full()
|
||||||
|
})
|
||||||
|
.flex_shrink_0()
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.id("slider-bar")
|
.id("slider-bar")
|
||||||
.relative()
|
.relative()
|
||||||
.w_full()
|
.when(self.axis.is_horizontal(), |this| this.w_full().h_1p5())
|
||||||
.my_1p5()
|
.when(self.axis.is_vertical(), |this| this.h_full().w_1p5())
|
||||||
.h_1p5()
|
|
||||||
.bg(cx.theme().slider_bar.opacity(0.2))
|
.bg(cx.theme().slider_bar.opacity(0.2))
|
||||||
.active(|this| this.bg(cx.theme().slider_bar.opacity(0.4)))
|
.active(|this| this.bg(cx.theme().slider_bar.opacity(0.4)))
|
||||||
.rounded(px(3.))
|
.rounded(px(3.))
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.absolute()
|
.absolute()
|
||||||
.top_0()
|
.when(!self.reverse, |this| this.top_0().left_0())
|
||||||
.left_0()
|
.when(self.reverse, |this| this.bottom_0().right_0())
|
||||||
.h_full()
|
.when(self.axis.is_horizontal(), |this| {
|
||||||
.w(relative(self.relative_value()))
|
this.h_full().w(thumb_bar_size)
|
||||||
|
})
|
||||||
|
.when(self.axis.is_vertical(), |this| {
|
||||||
|
this.w_full().h(thumb_bar_size)
|
||||||
|
})
|
||||||
.bg(cx.theme().slider_bar)
|
.bg(cx.theme().slider_bar)
|
||||||
.rounded_l(px(3.)),
|
.rounded_full(),
|
||||||
)
|
)
|
||||||
.child(self.render_thumb(cx))
|
.child(self.render_thumb(thumb_bar_size, cx))
|
||||||
.child({
|
.child({
|
||||||
let view = cx.view().clone();
|
let view = cx.view().clone();
|
||||||
canvas(
|
canvas(
|
||||||
|
|
|
||||||
|
|
@ -70,19 +70,31 @@ pub fn box_shadow(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub trait Colorize {
|
pub trait Colorize {
|
||||||
|
/// Returns a new color with the given opacity.
|
||||||
|
///
|
||||||
|
/// The opacity is a value between 0.0 and 1.0, where 0.0 is fully transparent and 1.0 is fully opaque.
|
||||||
fn opacity(&self, opacity: f32) -> Hsla;
|
fn opacity(&self, opacity: f32) -> Hsla;
|
||||||
|
/// Returns a new color with each channel divided by the given divisor.
|
||||||
|
///
|
||||||
|
/// The divisor in range of 0.0 .. 1.0
|
||||||
fn divide(&self, divisor: f32) -> Hsla;
|
fn divide(&self, divisor: f32) -> Hsla;
|
||||||
|
/// Return inverted color
|
||||||
fn invert(&self) -> Hsla;
|
fn invert(&self) -> Hsla;
|
||||||
|
/// Return inverted lightness
|
||||||
fn invert_l(&self) -> Hsla;
|
fn invert_l(&self) -> Hsla;
|
||||||
|
/// Return a new color with the lightness increased by the given factor.
|
||||||
|
///
|
||||||
|
/// factor range: 0.0 .. 1.0
|
||||||
fn lighten(&self, amount: f32) -> Hsla;
|
fn lighten(&self, amount: f32) -> Hsla;
|
||||||
|
/// Return a new color with the darkness increased by the given factor.
|
||||||
|
///
|
||||||
|
/// factor range: 0.0 .. 1.0
|
||||||
fn darken(&self, amount: f32) -> Hsla;
|
fn darken(&self, amount: f32) -> Hsla;
|
||||||
|
/// Return a new color with the same lightness and alpha but different hue and saturation.
|
||||||
fn apply(&self, base_color: Hsla) -> Hsla;
|
fn apply(&self, base_color: Hsla) -> Hsla;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Colorize for Hsla {
|
impl Colorize for Hsla {
|
||||||
/// Returns a new color with the given opacity.
|
|
||||||
///
|
|
||||||
/// The opacity is a value between 0.0 and 1.0, where 0.0 is fully transparent and 1.0 is fully opaque.
|
|
||||||
fn opacity(&self, factor: f32) -> Hsla {
|
fn opacity(&self, factor: f32) -> Hsla {
|
||||||
Hsla {
|
Hsla {
|
||||||
a: self.a * factor.clamp(0.0, 1.0),
|
a: self.a * factor.clamp(0.0, 1.0),
|
||||||
|
|
@ -90,9 +102,6 @@ impl Colorize for Hsla {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new color with each channel divided by the given divisor.
|
|
||||||
///
|
|
||||||
/// The divisor in range of 0.0 .. 1.0
|
|
||||||
fn divide(&self, divisor: f32) -> Hsla {
|
fn divide(&self, divisor: f32) -> Hsla {
|
||||||
Hsla {
|
Hsla {
|
||||||
a: divisor,
|
a: divisor,
|
||||||
|
|
@ -100,17 +109,15 @@ impl Colorize for Hsla {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return inverted color
|
|
||||||
fn invert(&self) -> Hsla {
|
fn invert(&self) -> Hsla {
|
||||||
Hsla {
|
Hsla {
|
||||||
h: (self.h + 1.8) % 3.6,
|
h: 1.0 - self.h,
|
||||||
s: 1.0 - self.s,
|
s: 1.0 - self.s,
|
||||||
l: 1.0 - self.l,
|
l: 1.0 - self.l,
|
||||||
a: self.a,
|
a: self.a,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return inverted lightness
|
|
||||||
fn invert_l(&self) -> Hsla {
|
fn invert_l(&self) -> Hsla {
|
||||||
Hsla {
|
Hsla {
|
||||||
l: 1.0 - self.l,
|
l: 1.0 - self.l,
|
||||||
|
|
@ -118,25 +125,18 @@ impl Colorize for Hsla {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a new color with the lightness increased by the given factor.
|
|
||||||
///
|
|
||||||
/// factor range: 0.0 .. 1.0
|
|
||||||
fn lighten(&self, factor: f32) -> Hsla {
|
fn lighten(&self, factor: f32) -> Hsla {
|
||||||
let l = self.l * (1.0 + factor.clamp(0.0, 1.0));
|
let l = self.l * (1.0 + factor.clamp(0.0, 1.0));
|
||||||
|
|
||||||
Hsla { l, ..*self }
|
Hsla { l, ..*self }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a new color with the darkness increased by the given factor.
|
|
||||||
///
|
|
||||||
/// factor range: 0.0 .. 1.0
|
|
||||||
fn darken(&self, factor: f32) -> Hsla {
|
fn darken(&self, factor: f32) -> Hsla {
|
||||||
let l = self.l * (1.0 - factor.clamp(0.0, 1.0));
|
let l = self.l * (1.0 - factor.clamp(0.0, 1.0));
|
||||||
|
|
||||||
Hsla { l, ..*self }
|
Hsla { l, ..*self }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a new color with the same lightness and alpha but different hue and saturation.
|
|
||||||
fn apply(&self, new_color: Hsla) -> Hsla {
|
fn apply(&self, new_color: Hsla) -> Hsla {
|
||||||
Hsla {
|
Hsla {
|
||||||
h: new_color.h,
|
h: new_color.h,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue