use std::rc::Rc;
use teksilo_canvas::{Canvas, Path, Point, Rect, Size, SizeProposal};
use teksilo_core::accessibility::AccessNodeBuilder;
use teksilo_core::binding::BindingLevel;
use teksilo_core::build_context::BuildContext;
use teksilo_core::color_prop::ColorProp;
use teksilo_core::widget::{
EventContext, LayoutContext, LayoutResponse, PaintContext, Widget, WidgetPlacement,
};
use teksilo_core::widget_builder::HandlerSet;
use teksilo_core::widget_id::WidgetId;
use teksilo_tokens::{SurfaceRole, TextRole};
use crate::primitives::rect_widget::RectWidget;
pub struct TwistArrow {
size: f32,
has_children: bool,
expanded: bool,
color: ColorProp,
on_click: Option<Rc<dyn Fn(&mut EventContext)>>,
}
impl TwistArrow {
pub fn new(size: f32, has_children: bool, expanded: bool) -> Self {
Self {
size,
has_children,
expanded,
color: ColorProp::TextRole(TextRole::Secondary),
on_click: None,
}
}
pub fn color(mut self, color: impl Into<ColorProp>) -> Self {
self.color = color.into();
self
}
pub fn on_click(mut self, f: impl Fn(&mut EventContext) + 'static) -> Self {
self.on_click = Some(Rc::new(f));
self
}
}
impl std::fmt::Debug for TwistArrow {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TwistArrow")
.field("size", &self.size)
.field("has_children", &self.has_children)
.field("expanded", &self.expanded)
.field("color", &self.color)
.finish()
}
}
impl Widget for TwistArrow {
fn build(&mut self, ctx: &mut BuildContext) -> Vec<WidgetId> {
self.color.register_if_bound(
ctx.self_id(),
ctx.binding_registry(),
BindingLevel::RepaintOnly,
);
if let Some(cb) = self.on_click.clone() {
let handlers = HandlerSet::new()
.on_tap(move |_pos, ctx| {
cb(ctx);
})
.focusable(false)
.gesture_dead_zone(true);
ctx.apply_self_handlers(handlers);
}
let rect = ctx.add(RectWidget::new().background(SurfaceRole::Transparent));
vec![rect]
}
fn layout_response(&self, _proposal: SizeProposal, _ctx: &LayoutContext) -> LayoutResponse {
Size::new(self.size, self.size).into()
}
fn place_children(
&self,
bounds: Rect,
_proposal: SizeProposal,
children: &mut [WidgetPlacement],
_ctx: &LayoutContext,
) {
for child in children.iter_mut() {
child.origin = bounds.origin();
child.size = bounds.size();
}
}
fn paint(&self, bounds: Rect, canvas: &mut Canvas, ctx: &PaintContext) {
if !self.has_children {
return;
}
let color = self.color.resolve(ctx.theme, ctx.effective_enabled);
let cx = bounds.x + bounds.width / 2.0;
let cy = bounds.y + bounds.height / 2.0;
let r = bounds.width.min(bounds.height) * 0.4;
let mut path = Path::new();
if self.expanded {
path.move_to(Point::new(cx - r, cy - r * 0.4));
path.line_to(Point::new(cx + r, cy - r * 0.4));
path.line_to(Point::new(cx, cy + r * 0.6));
path.close();
} else if ctx.layout_direction == teksilo_core::environment::LayoutDirection::RightToLeft {
path.move_to(Point::new(cx + r * 0.4, cy - r));
path.line_to(Point::new(cx - r * 0.6, cy));
path.line_to(Point::new(cx + r * 0.4, cy + r));
path.close();
} else {
path.move_to(Point::new(cx - r * 0.4, cy - r));
path.line_to(Point::new(cx + r * 0.6, cy));
path.line_to(Point::new(cx - r * 0.4, cy + r));
path.close();
}
canvas.fill_path(&path, color);
}
fn accessibility(&self, builder: &mut AccessNodeBuilder) {
builder.set_hidden();
}
}