checkbox.rs

  1#![allow(missing_docs)]
  2mod checkbox_with_label;
  3
  4pub use checkbox_with_label::*;
  5
  6use gpui::{div, prelude::*, ElementId, IntoElement, Styled, WindowContext};
  7
  8use crate::prelude::*;
  9use crate::{Color, Icon, IconName, Selection};
 10
 11/// # Checkbox
 12///
 13/// Checkboxes are used for multiple choices, not for mutually exclusive choices.
 14/// Each checkbox works independently from other checkboxes in the list,
 15/// therefore checking an additional box does not affect any other selections.
 16#[derive(IntoElement)]
 17pub struct Checkbox {
 18    id: ElementId,
 19    checked: Selection,
 20    disabled: bool,
 21    on_click: Option<Box<dyn Fn(&Selection, &mut WindowContext) + 'static>>,
 22}
 23
 24impl Checkbox {
 25    pub fn new(id: impl Into<ElementId>, checked: Selection) -> Self {
 26        Self {
 27            id: id.into(),
 28            checked,
 29            disabled: false,
 30            on_click: None,
 31        }
 32    }
 33
 34    pub fn disabled(mut self, disabled: bool) -> Self {
 35        self.disabled = disabled;
 36        self
 37    }
 38
 39    pub fn on_click(mut self, handler: impl Fn(&Selection, &mut WindowContext) + 'static) -> Self {
 40        self.on_click = Some(Box::new(handler));
 41        self
 42    }
 43}
 44
 45impl RenderOnce for Checkbox {
 46    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
 47        let group_id = format!("checkbox_group_{:?}", self.id);
 48
 49        let icon = match self.checked {
 50            Selection::Selected => Some(Icon::new(IconName::Check).size(IconSize::Small).color(
 51                if self.disabled {
 52                    Color::Disabled
 53                } else {
 54                    Color::Selected
 55                },
 56            )),
 57            Selection::Indeterminate => Some(
 58                Icon::new(IconName::Dash)
 59                    .size(IconSize::Small)
 60                    .color(if self.disabled {
 61                        Color::Disabled
 62                    } else {
 63                        Color::Selected
 64                    }),
 65            ),
 66            Selection::Unselected => None,
 67        };
 68
 69        let selected =
 70            self.checked == Selection::Selected || self.checked == Selection::Indeterminate;
 71
 72        let (bg_color, border_color) = match (self.disabled, selected) {
 73            (true, _) => (
 74                cx.theme().colors().ghost_element_disabled,
 75                cx.theme().colors().border_disabled,
 76            ),
 77            (false, true) => (
 78                cx.theme().colors().element_selected,
 79                cx.theme().colors().border,
 80            ),
 81            (false, false) => (
 82                cx.theme().colors().element_background,
 83                cx.theme().colors().border,
 84            ),
 85        };
 86
 87        h_flex()
 88            .id(self.id)
 89            .justify_center()
 90            .items_center()
 91            .size(crate::styles::custom_spacing(cx, 20.))
 92            .group(group_id.clone())
 93            .child(
 94                div()
 95                    .flex()
 96                    .flex_none()
 97                    .justify_center()
 98                    .items_center()
 99                    .m(Spacing::Small.px(cx))
100                    .size(crate::styles::custom_spacing(cx, 16.))
101                    .rounded_sm()
102                    .bg(bg_color)
103                    .border_1()
104                    .border_color(border_color)
105                    .when(!self.disabled, |this| {
106                        this.group_hover(group_id.clone(), |el| {
107                            el.bg(cx.theme().colors().element_hover)
108                        })
109                    })
110                    .children(icon),
111            )
112            .when_some(
113                self.on_click.filter(|_| !self.disabled),
114                |this, on_click| this.on_click(move |_, cx| on_click(&self.checked.inverse(), cx)),
115            )
116    }
117}