use crate::components::Props;
use crate::components::Event;
use crate::elements::element::Element;
use crate::elements::element_data::ElementData;
use crate::elements::element_styles::ElementStyles;
use crate::layout::layout_context::LayoutContext;
use crate::elements::thumb::Thumb;
use crate::events::CraftMessage;
use crate::geometry::Point;
use crate::reactive::element_state_store::{ElementStateStore, ElementStateStoreItem};
use crate::renderer::renderer::RenderList;
use crate::style::{Display, Style, Unit};
use crate::ComponentSpecification;
use crate::{generate_component_methods_no_children, palette};
use std::any::Any;
use std::sync::Arc;
use taffy::{NodeId, TaffyTree};
use winit::window::Window;
use crate::text::text_context::TextContext;
#[derive(Clone, Debug)]
pub struct Switch {
pub element_data: ElementData,
default_toggled: bool,
thumb: Thumb,
pub(crate) toggled_track_style: Style,
spacing: f32,
rounded: bool,
}
#[derive(Clone, Default)]
pub struct SwitchState {
pub(crate) toggled: Option<bool>,
}
impl Element for Switch {
fn element_data(&self) -> &ElementData {
&self.element_data
}
fn element_data_mut(&mut self) -> &mut ElementData {
&mut self.element_data
}
fn name(&self) -> &'static str {
"Switch"
}
fn draw(
&mut self,
renderer: &mut RenderList,
text_context: &mut TextContext,
taffy_tree: &mut TaffyTree<LayoutContext>,
_root_node: NodeId,
element_state: &mut ElementStateStore,
pointer: Option<Point>,
window: Option<Arc<dyn Window>>,
) {
if !self.element_data.style.visible() {
return;
}
self.draw_borders(renderer, element_state);
self.thumb.pseudo_thumb.draw(renderer, text_context, taffy_tree, _root_node, element_state, pointer, window);
}
fn compute_layout(
&mut self,
taffy_tree: &mut TaffyTree<LayoutContext>,
element_state: &mut ElementStateStore,
scale_factor: f64,
) -> Option<NodeId> {
let state = self.get_state(element_state);
self.merge_default_style();
let default_toggled = self.default_toggled;
let mut set_toggled_styles = || {
self.element_data_mut().style = Style::merge(&self.element_data().style, &self.default_toggled_style());
self.element_data_mut().style = Style::merge(&self.element_data().style, &self.toggled_track_style);
};
if let Some(toggled) = state.toggled {
if toggled {
set_toggled_styles();
}
} else if default_toggled {
set_toggled_styles();
}
let child_node = self.thumb.compute_layout(taffy_tree, element_state, scale_factor, state.toggled.unwrap_or(default_toggled), self.rounded);
let style: taffy::Style = self.element_data.style.to_taffy_style_with_scale_factor(scale_factor);
self.element_data_mut().taffy_node_id = Some(taffy_tree.new_with_children(style, &[child_node]).unwrap());
self.element_data().taffy_node_id
}
fn finalize_layout(
&mut self,
taffy_tree: &mut TaffyTree<LayoutContext>,
root_node: NodeId,
position: Point,
z_index: &mut u32,
transform: glam::Mat4,
element_state: &mut ElementStateStore,
pointer: Option<Point>,
text_context: &mut TextContext,
) {
let state = self.get_state(element_state);
let result = taffy_tree.layout(root_node).unwrap();
self.resolve_box(position, transform, result, z_index);
self.finalize_borders(element_state);
let x = if state.toggled.unwrap_or(self.default_toggled) {
self.element_data.computed_box.content_rectangle().right() - self.spacing - self.thumb.size
} else {
self.element_data.computed_box.content_rectangle().left() + self.spacing
};
let y = self.element_data.computed_box.content_rectangle().top() + self.spacing;
self.thumb.finalize_layout(
taffy_tree,
Point::new(x, y),
z_index,
transform,
element_state,
pointer,
text_context,
);
}
fn as_any(&self) -> &dyn Any {
self
}
fn on_event(
&self,
message: &CraftMessage,
element_state: &mut ElementStateStore,
_text_context: &mut TextContext,
should_style: bool,
) -> Event {
let mut ret = Event::default();
self.on_style_event(message, element_state, should_style);
let base_state = self.get_base_state_mut(element_state);
let state = base_state.data.as_mut().downcast_mut::<SwitchState>().unwrap();
if message.clicked() {
if let Some(toggled) = state.toggled {
state.toggled = Some(!toggled);
} else {
state.toggled = Some(!self.default_toggled);
}
ret.result_message(CraftMessage::SwitchToggled(state.toggled.unwrap()));
}
ret
}
fn initialize_state(&mut self, _scaling_factor: f64) -> ElementStateStoreItem {
ElementStateStoreItem {
base: Default::default(),
data: Box::new(SwitchState::default()),
}
}
fn default_style(&self) -> Style {
let mut style = Style::default();
let thumb_diameter = self.thumb.size;
let padding = self.spacing;
let width = thumb_diameter * 2.25;
let height = thumb_diameter + padding * 2.0;
*style.display_mut() = Display::Flex;
*style.width_mut() = Unit::Px(width);
*style.min_width_mut() = Unit::Px(width);
*style.height_mut() = Unit::Px(height);
*style.min_height_mut() = Unit::Px(height);
*style.background_mut() = palette::css::LIGHT_GRAY;
if self.rounded {
let rounding = self.thumb.size / 1.5;
*style.border_radius_mut() = [(rounding, rounding), (rounding, rounding), (rounding, rounding), (rounding, rounding)];
}
style
}
}
impl Default for Switch {
fn default() -> Self {
Self::new(26.0)
}
}
impl Switch {
pub fn spacing(mut self, amount: f32) -> Self {
self.spacing = amount;
self
}
pub fn round(mut self) -> Self {
self.rounded = true;
self
}
fn default_toggled_style(&self) -> Style {
let mut style = Style::default();
*style.background_mut() = palette::css::DODGER_BLUE;
Style::merge(&self.default_style(), &style)
}
pub fn thumb_style(mut self, thumb_style: Style) -> Self {
self.thumb.thumb_style(thumb_style);
self
}
pub fn toggled_thumb_style(mut self, toggled_thumb_style: Style) -> Self {
self.thumb.toggled_thumb_style(toggled_thumb_style);
self
}
pub fn toggled_style(mut self, toggled_thumb_style: Style) -> Self {
self.toggled_track_style = toggled_thumb_style;
self
}
pub fn default_toggled(mut self, default_toggled: bool) -> Self {
self.default_toggled = default_toggled;
self
}
#[allow(dead_code)]
fn get_state<'a>(&self, element_state: &'a ElementStateStore) -> &'a SwitchState {
element_state.storage.get(&self.element_data.component_id).unwrap().data.as_ref().downcast_ref().unwrap()
}
pub fn new(size: f32) -> Switch {
Switch {
element_data: Default::default(),
default_toggled: false,
thumb: Thumb {
pseudo_thumb: Default::default(),
toggled_thumb_style: Default::default(),
size,
},
toggled_track_style: Default::default(),
spacing: 4.0,
rounded: false,
}
}
generate_component_methods_no_children!();
}
impl ElementStyles for Switch {
fn styles_mut(&mut self) -> &mut Style {
self.element_data.current_style_mut()
}
}