mod layout;
mod node;
mod reconcile;
pub use layout::measure_checkbox;
pub use node::CheckboxNode;
pub use reconcile::reconcile_checkbox;
use std::sync::Arc;
use crate::callback::{Callback, KeyHandler};
use crate::core::element::{Element, ElementKind};
use crate::core::event::MouseEvent;
use crate::style::{Length, Padding, Style, StyleSlot};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum CheckboxVariant {
#[default]
Bracket,
Circle,
Box,
Custom {
checked: &'static str,
unchecked: &'static str,
indeterminate: &'static str,
},
}
impl CheckboxVariant {
pub fn checked_str(self) -> &'static str {
match self {
Self::Bracket => "[x]",
Self::Circle => "◉",
Self::Box => "✓",
Self::Custom { checked, .. } => checked,
}
}
pub fn unchecked_str(self) -> &'static str {
match self {
Self::Bracket => "[ ]",
Self::Circle => "○",
Self::Box => "☐",
Self::Custom { unchecked, .. } => unchecked,
}
}
pub fn indeterminate_str(self) -> &'static str {
match self {
Self::Bracket => "[-]",
Self::Circle => "◍",
Self::Box => "▣",
Self::Custom { indeterminate, .. } => indeterminate,
}
}
pub fn width(self) -> u16 {
use unicode_width::UnicodeWidthStr;
match self {
Self::Bracket => 3,
Self::Circle | Self::Box => 1,
Self::Custom {
checked,
unchecked,
indeterminate,
} => UnicodeWidthStr::width(checked)
.max(UnicodeWidthStr::width(unchecked))
.max(UnicodeWidthStr::width(indeterminate)) as u16,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum CheckboxState {
Unchecked,
Checked,
Indeterminate,
}
impl CheckboxState {
pub fn is_checked(self) -> bool {
matches!(self, Self::Checked)
}
pub fn is_indeterminate(self) -> bool {
matches!(self, Self::Indeterminate)
}
pub fn toggle(self) -> Self {
match self {
Self::Checked => Self::Unchecked,
Self::Unchecked | Self::Indeterminate => Self::Checked,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct CheckboxEvent {
pub state: CheckboxState,
}
#[derive(Clone)]
pub struct Checkbox {
pub state: CheckboxState,
pub label: Option<Arc<str>>,
pub variant: CheckboxVariant,
pub gap: u16,
pub style: Style,
pub hover_style: StyleSlot,
pub focus_style: StyleSlot,
pub checked_style: Style,
pub unchecked_style: Style,
pub indeterminate_style: Style,
pub label_style: Style,
pub padding: Padding,
pub disabled: bool,
pub disabled_style: Style,
pub width: Length,
pub height: Length,
pub on_toggle: Option<Callback<CheckboxEvent>>,
pub on_click: Option<Callback<MouseEvent>>,
pub on_key: Option<KeyHandler>,
pub focusable: bool,
}
impl Checkbox {
pub fn new(checked: bool) -> Self {
Self {
state: if checked {
CheckboxState::Checked
} else {
CheckboxState::Unchecked
},
label: None,
variant: CheckboxVariant::Bracket,
gap: 1,
style: Style::default(),
hover_style: StyleSlot::Inherit,
focus_style: StyleSlot::Inherit,
checked_style: Style::default(),
unchecked_style: Style::default(),
indeterminate_style: Style::default(),
label_style: Style::default(),
padding: Padding::default(),
disabled: false,
disabled_style: Style::default(),
width: Length::Auto,
height: Length::Auto,
on_toggle: None,
on_click: None,
on_key: None,
focusable: true,
}
}
pub fn state(mut self, state: CheckboxState) -> Self {
self.state = state;
self
}
pub fn checked(mut self, checked: bool) -> Self {
self.state = if checked {
CheckboxState::Checked
} else {
CheckboxState::Unchecked
};
self
}
pub fn indeterminate(mut self, indeterminate: bool) -> Self {
if indeterminate {
self.state = CheckboxState::Indeterminate;
} else if self.state.is_indeterminate() {
self.state = CheckboxState::Unchecked;
}
self
}
pub fn label(mut self, label: impl Into<Arc<str>>) -> Self {
self.label = Some(label.into());
self
}
pub fn variant(mut self, variant: CheckboxVariant) -> Self {
self.variant = variant;
self
}
pub fn gap(mut self, gap: u16) -> Self {
self.gap = gap;
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn hover_style(mut self, style: Style) -> Self {
self.hover_style = StyleSlot::Replace(style);
self
}
pub fn extend_hover_style(mut self, style: Style) -> Self {
self.hover_style = StyleSlot::Extend(style);
self
}
pub fn inherit_hover_style(mut self) -> Self {
self.hover_style = StyleSlot::Inherit;
self
}
pub fn hover_style_slot(mut self, slot: StyleSlot) -> Self {
self.hover_style = slot;
self
}
pub fn focus_style(mut self, style: Style) -> Self {
self.focus_style = StyleSlot::Replace(style);
self
}
pub fn extend_focus_style(mut self, style: Style) -> Self {
self.focus_style = StyleSlot::Extend(style);
self
}
pub fn inherit_focus_style(mut self) -> Self {
self.focus_style = StyleSlot::Inherit;
self
}
pub fn focus_style_slot(mut self, slot: StyleSlot) -> Self {
self.focus_style = slot;
self
}
pub fn checked_style(mut self, style: Style) -> Self {
self.checked_style = style;
self
}
pub fn unchecked_style(mut self, style: Style) -> Self {
self.unchecked_style = style;
self
}
pub fn indeterminate_style(mut self, style: Style) -> Self {
self.indeterminate_style = style;
self
}
pub fn label_style(mut self, style: Style) -> Self {
self.label_style = style;
self
}
pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
self.padding = padding.into();
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub fn disabled_style(mut self, style: Style) -> Self {
self.disabled_style = style;
self
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
pub fn on_toggle(mut self, cb: Callback<CheckboxEvent>) -> Self {
self.on_toggle = Some(cb);
self
}
pub fn on_click(mut self, cb: Callback<MouseEvent>) -> Self {
self.on_click = Some(cb);
self
}
pub fn on_key(mut self, handler: KeyHandler) -> Self {
self.on_key = Some(handler);
self
}
pub fn focusable(mut self, focusable: bool) -> Self {
self.focusable = focusable;
self
}
}
impl From<Checkbox> for Element {
fn from(value: Checkbox) -> Self {
Element::new(ElementKind::Checkbox(value))
}
}
impl crate::layout::hash::LayoutHash for Checkbox {
fn layout_hash(
&self,
hasher: &mut impl std::hash::Hasher,
_recurse: &dyn Fn(&Element) -> Option<u64>,
) -> Option<()> {
use std::hash::Hash;
self.width.hash(hasher);
self.height.hash(hasher);
self.variant.hash(hasher);
self.gap.hash(hasher);
self.padding.hash(hasher);
self.label.hash(hasher);
Some(())
}
}