use skia_safe::Color;
use crate::control::{Control, Has};
use crate::controls::control_style::PrebuiltControlStyle;
use crate::controls::layout::LayoutProps;
use crate::controls::shape::{ShapeBuild, ShapeSet, ShapeType, SkiaShape};
use crate::controls::svg::{SkiaSvg, SvgBuild, SvgSet};
use crate::controls::toggle::{SkiaToggle, ToggleColor, ToggleProps};
use crate::props;
use crate::tree::{Build, ControlId, Cx, Detached, Handle};
use crate::types::CacheType;
use crate::ui::Aria;
const SVG_CUPERTINO_CHECK: &str = r##"<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none"><path d="M4 12.6111L8.92308 17.5L20 6.5" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>"##;
const SVG_MATERIAL_CHECK: &str = r##"<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none"><path d="M5 13L9 17L19 7" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>"##;
const SVG_WINDOWS_CHECK: &str = r##"<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none"><path d="M4 11.6L10 17.6L20 7.6" stroke="#000000" stroke-width="2.5" stroke-linecap="square" stroke-linejoin="round"/></svg>"##;
const DEFAULT_ACCENT: Color = Color::from_rgb(0xDC, 0x14, 0x3C);
const DEFAULT_OUTLINE: Color = Color::from_rgb(0x8E, 0x95, 0x9D);
props!(CheckboxProps, CheckboxBuild, CheckboxSet {
color_check_on / set_color_check_on: Option<Color> = None, APPLY;
});
pub struct SkiaCheckbox {
toggle: SkiaToggle,
pub p: CheckboxProps,
frame_off: Handle<SkiaShape>,
frame_on: Handle<SkiaShape>,
check: ControlId,
}
impl SkiaCheckbox {
#[allow(clippy::new_ret_no_self)]
pub fn new() -> Build<SkiaCheckbox> {
let checkbox = SkiaCheckbox {
toggle: SkiaToggle::new(),
p: CheckboxProps::default(),
frame_off: Handle::default(),
frame_on: Handle::default(),
check: Handle::<SkiaShape>::default().id(),
};
let mut build = Build::new(checkbox);
let id = build.id();
build.control_mut().toggle.id = id;
build
}
fn style_default(style: PrebuiltControlStyle, which: ToggleColor) -> Option<Color> {
use PrebuiltControlStyle::*;
use ToggleColor::*;
let rgb = |r, g, b| Some(Color::from_rgb(r, g, b));
match (style, which) {
(Cupertino, FrameOff) => rgb(0xBF, 0xBF, 0xBF),
(Cupertino, FrameOn) => rgb(0x00, 0x7A, 0xFF),
(Material, FrameOff) => rgb(0x75, 0x75, 0x75),
(Material, FrameOn) => rgb(0x21, 0x96, 0xF3),
(Material3, FrameOff) => rgb(0x49, 0x45, 0x4F),
(Material3, FrameOn) => rgb(0x67, 0x50, 0xA4),
(Windows, FrameOff) => rgb(0x99, 0x99, 0x99),
(Windows, FrameOn) => rgb(0x00, 0x78, 0xD7),
(_, FrameOff) => Some(DEFAULT_OUTLINE),
(_, FrameOn) => Some(DEFAULT_ACCENT),
_ => None,
}
}
fn color(&self, which: ToggleColor) -> Color {
self.toggle.color(which, Self::style_default(self.toggle.using_control_style(), which))
}
pub fn color_check_on(&self) -> Color {
let default = if self.toggle.using_control_style() == PrebuiltControlStyle::Unset { DEFAULT_ACCENT } else { Color::WHITE };
self.p.color_check_on.unwrap_or(default)
}
fn build_content(&mut self, cx: &mut Cx) {
let mut size = 22.0;
let (mut off_handle, mut on_handle, mut check_id) = (Handle::default(), Handle::default(), self.check);
self.toggle.rebuild(cx, |style| {
use PrebuiltControlStyle::*;
let mut off = SkiaShape::new().tag("FrameOff").shape_type(ShapeType::Rectangle).fill();
let mut on = SkiaShape::new().tag("FrameOn").shape_type(ShapeType::Rectangle).fill();
let mut svg = |markup: &str, margin: f32| -> Detached {
let view = SkiaSvg::from_string(markup).tag("ViewCheckOn").tint_color(Color::WHITE).margin(margin).fill();
check_id = view.id();
view.into()
};
let check = match style {
Cupertino => {
size = 22.0;
off = off.stroke_width(1.5).corner_radius(4.0);
on = on.corner_radius(4.0);
svg(SVG_CUPERTINO_CHECK, 2.0)
}
Material => {
size = 24.0;
off = off.stroke_width(2.0).corner_radius(2.0);
on = on.corner_radius(2.0);
svg(SVG_MATERIAL_CHECK, 2.0)
}
Material3 => {
size = 18.0;
off = off.stroke_width(2.0).corner_radius(2.0);
on = on.corner_radius(2.0);
svg(SVG_MATERIAL_CHECK, 2.0)
}
Windows => {
size = 20.0;
off = off.stroke_width(1.0);
svg(SVG_WINDOWS_CHECK, 1.0)
}
_ => {
off = off.stroke_width(1.0);
on = on.stroke_width(1.0);
let square = SkiaShape::new()
.tag("ViewCheckOn")
.shape_type(ShapeType::Rectangle)
.margin(3.0)
.lock_ratio(1.0)
.fill()
.use_cache(CacheType::Operations);
check_id = square.id();
square.into()
}
};
let on = on.assign(&mut on_handle).children(check);
vec![off.assign(&mut off_handle).into(), on.into()]
});
(self.frame_off, self.frame_on, self.check) = (off_handle, on_handle, check_id);
self.toggle.pin_size(cx, size, size);
}
fn apply(&mut self, cx: &mut Cx) {
let on = self.toggle.p.is_toggled;
let (frame_on, frame_off, check) = (self.color(ToggleColor::FrameOn), self.color(ToggleColor::FrameOff), self.color_check_on());
let flat = self.toggle.using_control_style() == PrebuiltControlStyle::Unset;
if let Some(mut off) = cx.get_mut(self.frame_off) {
off.set_stroke_color(frame_off);
off.set_is_visible(!on);
}
if let Some(mut frame) = cx.get_mut(self.frame_on) {
if flat {
frame.set_stroke_color(frame_on);
} else {
frame.set_background_color(frame_on);
}
frame.set_is_visible(on);
}
if flat {
if let Some(mut square) = cx.tree.find_mut::<SkiaShape>(self.check) {
square.set_background_color(check);
}
} else if let Some(mut svg) = cx.tree.find_mut::<SkiaSvg>(self.check) {
svg.set_tint_color(check);
}
if let Some(mut mark) = cx.any_mut(self.check) {
mark.set_is_visible(on);
}
}
}
impl Has<CheckboxProps> for SkiaCheckbox {
fn part(&self) -> &CheckboxProps {
&self.p
}
fn part_mut(&mut self) -> &mut CheckboxProps {
&mut self.p
}
}
impl Has<ToggleProps> for SkiaCheckbox {
fn part(&self) -> &ToggleProps {
&self.toggle.p
}
fn part_mut(&mut self) -> &mut ToggleProps {
&mut self.toggle.p
}
}
impl Has<LayoutProps> for SkiaCheckbox {
fn part(&self) -> &LayoutProps {
Has::<LayoutProps>::part(&self.toggle)
}
fn part_mut(&mut self) -> &mut LayoutProps {
Has::<LayoutProps>::part_mut(&mut self.toggle)
}
}
impl Control for SkiaCheckbox {
fn inner(&self) -> Option<&dyn Control> {
Some(&self.toggle)
}
fn inner_mut(&mut self) -> Option<&mut dyn Control> {
Some(&mut self.toggle)
}
fn on_props_changed(&mut self, cx: &mut Cx) {
if self.toggle.needs_content() {
self.build_content(cx);
}
self.toggle.take_change(cx);
self.apply(cx);
}
fn accessibility_role(&self) -> Option<&'static str> {
Some(Aria::CHECKBOX)
}
}