ui: Add .color
method to the Switch (#29074)
This allows to pass, for example, `.color(SwitchColor::Accent)` to the Switch component and have it render differently. <img src="https://github.com/user-attachments/assets/c60bac8a-c5ae-4693-912a-c754e5081f45" width="550"/> Release Notes: - N/A
This commit is contained in:
parent
9db0c4f19a
commit
bfb2ed3824
1 changed files with 137 additions and 23 deletions
|
@ -339,6 +339,68 @@ impl RenderOnce for CheckboxWithLabel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines the color for a switch component.
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
|
||||||
|
pub enum SwitchColor {
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
Accent,
|
||||||
|
Error,
|
||||||
|
Warning,
|
||||||
|
Success,
|
||||||
|
Custom(Hsla),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwitchColor {
|
||||||
|
fn get_colors(&self, is_on: bool, cx: &App) -> (Hsla, Hsla) {
|
||||||
|
if !is_on {
|
||||||
|
return (
|
||||||
|
cx.theme().colors().element_disabled,
|
||||||
|
cx.theme().colors().border,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
SwitchColor::Default => {
|
||||||
|
let colors = cx.theme().colors();
|
||||||
|
let base_color = colors.text;
|
||||||
|
let bg_color = colors.element_background.blend(base_color.opacity(0.08));
|
||||||
|
(bg_color, colors.border_variant)
|
||||||
|
}
|
||||||
|
SwitchColor::Accent => {
|
||||||
|
let status = cx.theme().status();
|
||||||
|
(status.info.opacity(0.4), status.info.opacity(0.2))
|
||||||
|
}
|
||||||
|
SwitchColor::Error => {
|
||||||
|
let status = cx.theme().status();
|
||||||
|
(status.error.opacity(0.4), status.error.opacity(0.2))
|
||||||
|
}
|
||||||
|
SwitchColor::Warning => {
|
||||||
|
let status = cx.theme().status();
|
||||||
|
(status.warning.opacity(0.4), status.warning.opacity(0.2))
|
||||||
|
}
|
||||||
|
SwitchColor::Success => {
|
||||||
|
let status = cx.theme().status();
|
||||||
|
(status.success.opacity(0.4), status.success.opacity(0.2))
|
||||||
|
}
|
||||||
|
SwitchColor::Custom(color) => (*color, color.opacity(0.6)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SwitchColor> for Color {
|
||||||
|
fn from(color: SwitchColor) -> Self {
|
||||||
|
match color {
|
||||||
|
SwitchColor::Default => Color::Default,
|
||||||
|
SwitchColor::Accent => Color::Accent,
|
||||||
|
SwitchColor::Error => Color::Error,
|
||||||
|
SwitchColor::Warning => Color::Warning,
|
||||||
|
SwitchColor::Success => Color::Success,
|
||||||
|
SwitchColor::Custom(_) => Color::Default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// # Switch
|
/// # Switch
|
||||||
///
|
///
|
||||||
/// Switches are used to represent opposite states, such as enabled or disabled.
|
/// Switches are used to represent opposite states, such as enabled or disabled.
|
||||||
|
@ -350,6 +412,7 @@ pub struct Switch {
|
||||||
on_click: Option<Box<dyn Fn(&ToggleState, &mut Window, &mut App) + 'static>>,
|
on_click: Option<Box<dyn Fn(&ToggleState, &mut Window, &mut App) + 'static>>,
|
||||||
label: Option<SharedString>,
|
label: Option<SharedString>,
|
||||||
key_binding: Option<KeyBinding>,
|
key_binding: Option<KeyBinding>,
|
||||||
|
color: SwitchColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Switch {
|
impl Switch {
|
||||||
|
@ -362,9 +425,16 @@ impl Switch {
|
||||||
on_click: None,
|
on_click: None,
|
||||||
label: None,
|
label: None,
|
||||||
key_binding: None,
|
key_binding: None,
|
||||||
|
color: SwitchColor::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the color of the switch using the specified [`SwitchColor`].
|
||||||
|
pub fn color(mut self, color: SwitchColor) -> Self {
|
||||||
|
self.color = color;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the disabled state of the [`Switch`].
|
/// Sets the disabled state of the [`Switch`].
|
||||||
pub fn disabled(mut self, disabled: bool) -> Self {
|
pub fn disabled(mut self, disabled: bool) -> Self {
|
||||||
self.disabled = disabled;
|
self.disabled = disabled;
|
||||||
|
@ -397,25 +467,17 @@ impl RenderOnce for Switch {
|
||||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||||
let is_on = self.toggle_state == ToggleState::Selected;
|
let is_on = self.toggle_state == ToggleState::Selected;
|
||||||
let adjust_ratio = if is_light(cx) { 1.5 } else { 1.0 };
|
let adjust_ratio = if is_light(cx) { 1.5 } else { 1.0 };
|
||||||
let base_color = cx.theme().colors().text;
|
|
||||||
|
|
||||||
let bg_color = if is_on {
|
let base_color = cx.theme().colors().text;
|
||||||
cx.theme()
|
let thumb_color = base_color;
|
||||||
.colors()
|
let (bg_color, border_color) = self.color.get_colors(is_on, cx);
|
||||||
.element_background
|
|
||||||
.blend(base_color.opacity(0.08))
|
let bg_hover_color = if is_on {
|
||||||
|
bg_color.blend(base_color.opacity(0.16 * adjust_ratio))
|
||||||
} else {
|
} else {
|
||||||
cx.theme().colors().element_background
|
bg_color.blend(base_color.opacity(0.05 * adjust_ratio))
|
||||||
};
|
|
||||||
let thumb_color = base_color.opacity(0.8);
|
|
||||||
let thumb_hover_color = base_color;
|
|
||||||
let border_color = cx.theme().colors().border_variant;
|
|
||||||
// Lighter themes need higher contrast borders
|
|
||||||
let border_hover_color = if is_on {
|
|
||||||
border_color.blend(base_color.opacity(0.16 * adjust_ratio))
|
|
||||||
} else {
|
|
||||||
border_color.blend(base_color.opacity(0.05 * adjust_ratio))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let thumb_opacity = match (is_on, self.disabled) {
|
let thumb_opacity = match (is_on, self.disabled) {
|
||||||
(_, true) => 0.2,
|
(_, true) => 0.2,
|
||||||
(true, false) => 1.0,
|
(true, false) => 1.0,
|
||||||
|
@ -432,24 +494,20 @@ impl RenderOnce for Switch {
|
||||||
h_flex()
|
h_flex()
|
||||||
.when(is_on, |on| on.justify_end())
|
.when(is_on, |on| on.justify_end())
|
||||||
.when(!is_on, |off| off.justify_start())
|
.when(!is_on, |off| off.justify_start())
|
||||||
.items_center()
|
|
||||||
.size_full()
|
.size_full()
|
||||||
.rounded_full()
|
.rounded_full()
|
||||||
.px(DynamicSpacing::Base02.px(cx))
|
.px(DynamicSpacing::Base02.px(cx))
|
||||||
.bg(bg_color)
|
.bg(bg_color)
|
||||||
|
.when(!self.disabled, |this| {
|
||||||
|
this.group_hover(group_id.clone(), |el| el.bg(bg_hover_color))
|
||||||
|
})
|
||||||
.border_1()
|
.border_1()
|
||||||
.border_color(border_color)
|
.border_color(border_color)
|
||||||
.when(!self.disabled, |this| {
|
|
||||||
this.group_hover(group_id.clone(), |el| el.border_color(border_hover_color))
|
|
||||||
})
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.size(DynamicSpacing::Base12.rems(cx))
|
.size(DynamicSpacing::Base12.rems(cx))
|
||||||
.rounded_full()
|
.rounded_full()
|
||||||
.bg(thumb_color)
|
.bg(thumb_color)
|
||||||
.when(!self.disabled, |this| {
|
|
||||||
this.group_hover(group_id.clone(), |el| el.bg(thumb_hover_color))
|
|
||||||
})
|
|
||||||
.opacity(thumb_opacity),
|
.opacity(thumb_opacity),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -482,6 +540,7 @@ pub struct SwitchWithLabel {
|
||||||
toggle_state: ToggleState,
|
toggle_state: ToggleState,
|
||||||
on_click: Arc<dyn Fn(&ToggleState, &mut Window, &mut App) + 'static>,
|
on_click: Arc<dyn Fn(&ToggleState, &mut Window, &mut App) + 'static>,
|
||||||
disabled: bool,
|
disabled: bool,
|
||||||
|
color: SwitchColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SwitchWithLabel {
|
impl SwitchWithLabel {
|
||||||
|
@ -498,6 +557,7 @@ impl SwitchWithLabel {
|
||||||
toggle_state: toggle_state.into(),
|
toggle_state: toggle_state.into(),
|
||||||
on_click: Arc::new(on_click),
|
on_click: Arc::new(on_click),
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
color: SwitchColor::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,6 +566,12 @@ impl SwitchWithLabel {
|
||||||
self.disabled = disabled;
|
self.disabled = disabled;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the color of the switch using the specified [`SwitchColor`].
|
||||||
|
pub fn color(mut self, color: SwitchColor) -> Self {
|
||||||
|
self.color = color;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderOnce for SwitchWithLabel {
|
impl RenderOnce for SwitchWithLabel {
|
||||||
|
@ -516,6 +582,7 @@ impl RenderOnce for SwitchWithLabel {
|
||||||
.child(
|
.child(
|
||||||
Switch::new(self.id.clone(), self.toggle_state)
|
Switch::new(self.id.clone(), self.toggle_state)
|
||||||
.disabled(self.disabled)
|
.disabled(self.disabled)
|
||||||
|
.color(self.color)
|
||||||
.on_click({
|
.on_click({
|
||||||
let on_click = self.on_click.clone();
|
let on_click = self.on_click.clone();
|
||||||
move |checked, window, cx| {
|
move |checked, window, cx| {
|
||||||
|
@ -667,6 +734,53 @@ impl Component for Switch {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
example_group_with_title(
|
||||||
|
"Colors",
|
||||||
|
vec![
|
||||||
|
single_example(
|
||||||
|
"Default",
|
||||||
|
Switch::new("switch_default_style", ToggleState::Selected)
|
||||||
|
.color(SwitchColor::Default)
|
||||||
|
.on_click(|_, _, _cx| {})
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Accent",
|
||||||
|
Switch::new("switch_accent_style", ToggleState::Selected)
|
||||||
|
.color(SwitchColor::Accent)
|
||||||
|
.on_click(|_, _, _cx| {})
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Error",
|
||||||
|
Switch::new("switch_error_style", ToggleState::Selected)
|
||||||
|
.color(SwitchColor::Error)
|
||||||
|
.on_click(|_, _, _cx| {})
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Warning",
|
||||||
|
Switch::new("switch_warning_style", ToggleState::Selected)
|
||||||
|
.color(SwitchColor::Warning)
|
||||||
|
.on_click(|_, _, _cx| {})
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Success",
|
||||||
|
Switch::new("switch_success_style", ToggleState::Selected)
|
||||||
|
.color(SwitchColor::Success)
|
||||||
|
.on_click(|_, _, _cx| {})
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
single_example(
|
||||||
|
"Custom",
|
||||||
|
Switch::new("switch_custom_style", ToggleState::Selected)
|
||||||
|
.color(SwitchColor::Custom(hsla(300.0 / 360.0, 0.6, 0.6, 1.0)))
|
||||||
|
.on_click(|_, _, _cx| {})
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
example_group_with_title(
|
example_group_with_title(
|
||||||
"Disabled",
|
"Disabled",
|
||||||
vec![
|
vec![
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue