#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Color {
#[default]
Default,
Accent,
Success,
Warning,
Danger,
}
impl Color {
pub const ALL: [Color; 5] = [
Color::Default,
Color::Accent,
Color::Success,
Color::Warning,
Color::Danger,
];
pub fn token(self) -> &'static str {
match self {
Color::Default => "default",
Color::Accent => "accent",
Color::Success => "success",
Color::Warning => "warning",
Color::Danger => "danger",
}
}
pub fn label(self) -> &'static str {
match self {
Color::Default => "Default",
Color::Accent => "Accent",
Color::Success => "Success",
Color::Warning => "Warning",
Color::Danger => "Danger",
}
}
pub fn from_token(token: &str) -> Option<Color> {
Color::ALL.into_iter().find(|c| c.token() == token)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnknownColorError {
pub name: String,
}
impl std::fmt::Display for UnknownColorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"unknown colour role {:?} (expected default, accent, success, warning or danger)",
self.name
)
}
}
impl std::error::Error for UnknownColorError {}
impl std::str::FromStr for Color {
type Err = UnknownColorError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Color::from_token(s).ok_or_else(|| UnknownColorError { name: s.to_owned() })
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Variant {
#[default]
Primary,
Secondary,
Tertiary,
Outline,
Ghost,
Danger,
DangerSoft,
}
impl Variant {
pub const ALL: [Variant; 7] = [
Variant::Primary,
Variant::Secondary,
Variant::Tertiary,
Variant::Outline,
Variant::Ghost,
Variant::Danger,
Variant::DangerSoft,
];
pub const GROUP: [Variant; 7] = Self::ALL;
pub fn label(self) -> &'static str {
match self {
Variant::Primary => "Primary",
Variant::Secondary => "Secondary",
Variant::Tertiary => "Tertiary",
Variant::Outline => "Outline",
Variant::Ghost => "Ghost",
Variant::Danger => "Danger",
Variant::DangerSoft => "Danger Soft",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum FieldVariant {
#[default]
Primary,
Secondary,
}
impl FieldVariant {
pub const ALL: [FieldVariant; 2] = [FieldVariant::Primary, FieldVariant::Secondary];
pub fn label(self) -> &'static str {
match self {
FieldVariant::Primary => "Primary",
FieldVariant::Secondary => "Secondary",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Prominence {
Transparent,
#[default]
Default,
Secondary,
Tertiary,
}
impl Prominence {
pub const ALL: [Prominence; 4] = [
Prominence::Transparent,
Prominence::Default,
Prominence::Secondary,
Prominence::Tertiary,
];
pub fn label(self) -> &'static str {
match self {
Prominence::Transparent => "Transparent",
Prominence::Default => "Default",
Prominence::Secondary => "Secondary",
Prominence::Tertiary => "Tertiary",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Backdrop {
#[default]
Opaque,
Blur,
Transparent,
}
impl Backdrop {
pub const ALL: [Backdrop; 3] = [Backdrop::Opaque, Backdrop::Blur, Backdrop::Transparent];
pub fn label(self) -> &'static str {
match self {
Backdrop::Opaque => "Opaque",
Backdrop::Blur => "Blur",
Backdrop::Transparent => "Transparent",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Size {
Sm,
#[default]
Md,
Lg,
}
impl Size {
pub const ALL: [Size; 3] = [Size::Sm, Size::Md, Size::Lg];
pub fn control_height(self) -> gpui::Pixels {
match self {
Size::Sm => gpui::px(32.0),
Size::Md => gpui::px(36.0),
Size::Lg => gpui::px(40.0),
}
}
pub fn icon_control_size(self) -> gpui::Pixels {
self.control_height()
}
pub fn text_size(self) -> gpui::Pixels {
match self {
Size::Sm => gpui::px(12.0),
Size::Md => gpui::px(14.0),
Size::Lg => gpui::px(16.0),
}
}
pub fn label(self) -> &'static str {
match self {
Size::Sm => "Small",
Size::Md => "Medium",
Size::Lg => "Large",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum SizeXl {
Xs,
Sm,
#[default]
Md,
Lg,
Xl,
}
impl SizeXl {
pub const ALL: [SizeXl; 5] = [SizeXl::Xs, SizeXl::Sm, SizeXl::Md, SizeXl::Lg, SizeXl::Xl];
pub fn swatch_px(self) -> gpui::Pixels {
match self {
SizeXl::Xs => gpui::px(16.0),
SizeXl::Sm => gpui::px(24.0),
SizeXl::Md => gpui::px(32.0),
SizeXl::Lg => gpui::px(36.0),
SizeXl::Xl => gpui::px(40.0),
}
}
pub fn label(self) -> &'static str {
match self {
SizeXl::Xs => "Xs",
SizeXl::Sm => "Sm",
SizeXl::Md => "Md",
SizeXl::Lg => "Lg",
SizeXl::Xl => "Xl",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Orientation {
#[default]
Horizontal,
Vertical,
}
impl Orientation {
pub const ALL: [Orientation; 2] = [Orientation::Horizontal, Orientation::Vertical];
pub fn is_horizontal(self) -> bool {
matches!(self, Orientation::Horizontal)
}
pub fn label(self) -> &'static str {
match self {
Orientation::Horizontal => "Horizontal",
Orientation::Vertical => "Vertical",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum SelectionMode {
None,
#[default]
Single,
Multiple,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum SelectionBehavior {
#[default]
Toggle,
Replace,
}
impl SelectionBehavior {
pub const ALL: [SelectionBehavior; 2] = [SelectionBehavior::Toggle, SelectionBehavior::Replace];
pub fn label(self) -> &'static str {
match self {
SelectionBehavior::Toggle => "Toggle",
SelectionBehavior::Replace => "Replace",
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Placement {
Bottom,
#[default]
BottomStart,
BottomLeft,
BottomEnd,
BottomRight,
Top,
TopStart,
TopLeft,
TopEnd,
TopRight,
Left,
LeftTop,
LeftBottom,
Right,
RightTop,
RightBottom,
Start,
StartTop,
StartBottom,
End,
EndTop,
EndBottom,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PlacementAlign {
Start,
Center,
End,
}
impl Placement {
pub const ALL: [Placement; 22] = [
Placement::Bottom,
Placement::BottomStart,
Placement::BottomLeft,
Placement::BottomEnd,
Placement::BottomRight,
Placement::Top,
Placement::TopStart,
Placement::TopLeft,
Placement::TopEnd,
Placement::TopRight,
Placement::Left,
Placement::LeftTop,
Placement::LeftBottom,
Placement::Right,
Placement::RightTop,
Placement::RightBottom,
Placement::Start,
Placement::StartTop,
Placement::StartBottom,
Placement::End,
Placement::EndTop,
Placement::EndBottom,
];
pub fn label(self) -> &'static str {
match self {
Placement::Bottom => "Bottom",
Placement::BottomStart => "Bottom start",
Placement::BottomLeft => "Bottom left",
Placement::BottomEnd => "Bottom end",
Placement::BottomRight => "Bottom right",
Placement::Top => "Top",
Placement::TopStart => "Top start",
Placement::TopLeft => "Top left",
Placement::TopEnd => "Top end",
Placement::TopRight => "Top right",
Placement::Left => "Left",
Placement::LeftTop => "Left top",
Placement::LeftBottom => "Left bottom",
Placement::Right => "Right",
Placement::RightTop => "Right top",
Placement::RightBottom => "Right bottom",
Placement::Start => "Start",
Placement::StartTop => "Start top",
Placement::StartBottom => "Start bottom",
Placement::End => "End",
Placement::EndTop => "End top",
Placement::EndBottom => "End bottom",
}
}
pub fn is_above(self) -> bool {
matches!(
self,
Placement::Top
| Placement::TopStart
| Placement::TopLeft
| Placement::TopEnd
| Placement::TopRight
)
}
pub fn is_side(self) -> bool {
matches!(
self,
Placement::Left
| Placement::LeftTop
| Placement::LeftBottom
| Placement::Right
| Placement::RightTop
| Placement::RightBottom
| Placement::Start
| Placement::StartTop
| Placement::StartBottom
| Placement::End
| Placement::EndTop
| Placement::EndBottom
)
}
pub fn is_start_side(self) -> bool {
matches!(
self,
Placement::Left
| Placement::LeftTop
| Placement::LeftBottom
| Placement::Start
| Placement::StartTop
| Placement::StartBottom
)
}
pub fn align(self) -> PlacementAlign {
match self {
Placement::BottomStart
| Placement::BottomLeft
| Placement::TopStart
| Placement::TopLeft
| Placement::LeftTop
| Placement::RightTop
| Placement::StartTop
| Placement::EndTop => PlacementAlign::Start,
Placement::BottomEnd
| Placement::BottomRight
| Placement::TopEnd
| Placement::TopRight
| Placement::LeftBottom
| Placement::RightBottom
| Placement::StartBottom
| Placement::EndBottom => PlacementAlign::End,
_ => PlacementAlign::Center,
}
}
}
#[cfg(test)]
mod color_token_tests {
use super::*;
#[test]
fn every_role_round_trips_through_its_token() {
for color in Color::ALL {
assert_eq!(Color::from_token(color.token()), Some(color));
assert_eq!(color.token().parse::<Color>(), Ok(color));
}
}
#[test]
fn a_misspelt_role_is_an_error_not_accent() {
assert_eq!(Color::from_token("sucess"), None);
assert_eq!(Color::from_token("Accent"), None);
let err = "primary".parse::<Color>().unwrap_err();
assert_eq!(err.name, "primary");
assert!(err.to_string().contains("\"primary\""), "{err}");
}
}