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::signal::Signal;
use teksilo_core::widget::{LayoutContext, PaintContext, Widget};
use teksilo_core::widget_id::WidgetId;
const DWELL_INDICATOR_SIZE: f32 = 14.0;
pub(crate) struct DwellIndicator {
step: Signal<u32>,
sticky: Signal<bool>,
color: ColorProp,
}
impl std::fmt::Debug for DwellIndicator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DwellIndicator")
.field("step", &self.step.get())
.field("sticky", &self.sticky.get())
.finish()
}
}
impl DwellIndicator {
pub(crate) fn new(
step: Signal<u32>,
sticky: Signal<bool>,
color: impl Into<ColorProp>,
) -> Self {
Self {
step,
sticky,
color: color.into(),
}
}
}
impl Widget for DwellIndicator {
fn build(&mut self, ctx: &mut BuildContext) -> Vec<WidgetId> {
let self_id = ctx.self_id();
let registry = ctx.binding_registry();
self.step
.bind_to(self_id, registry, BindingLevel::RepaintOnly);
self.sticky
.bind_to(self_id, registry, BindingLevel::RepaintOnly);
Vec::new()
}
fn layout_response(
&self,
_proposal: SizeProposal,
_ctx: &LayoutContext,
) -> teksilo_core::widget::LayoutResponse {
Size::new(DWELL_INDICATOR_SIZE, DWELL_INDICATOR_SIZE).into()
}
fn paint(&self, bounds: Rect, canvas: &mut Canvas, ctx: &PaintContext) {
let center = Point::new(
bounds.x + bounds.width / 2.0,
bounds.y + bounds.height / 2.0,
);
let radius = (bounds.width.min(bounds.height) / 2.0) - 1.0;
let color = self.color.resolve(ctx.theme, true);
let sticky = self.sticky.get();
if sticky {
let head_r = radius * 0.55;
let head_center = Point::new(center.x, center.y - radius * 0.15);
canvas.fill_circle(head_center, head_r, color);
let tail_top_y = head_center.y + head_r * 0.4;
let tail_bottom = Point::new(center.x, center.y + radius * 0.95);
let mut tail = Path::new();
tail.move_to(Point::new(center.x - head_r * 0.55, tail_top_y));
tail.line_to(Point::new(center.x + head_r * 0.55, tail_top_y));
tail.line_to(tail_bottom);
tail.close();
canvas.fill_path(&tail, color);
return;
}
let step = self.step.get().min(crate::tooltip::rich::DWELL_STEPS);
canvas.stroke_circle(
center,
radius,
color,
teksilo_canvas::paint::StrokeStyle::solid(1.5),
);
if step == 0 {
return;
}
let inscribed = Rect::new(
center.x - radius,
center.y - radius,
radius * 2.0,
radius * 2.0,
);
let start_angle = -90.0;
let sweep_angle = 360.0 * (step as f32) / (crate::tooltip::rich::DWELL_STEPS as f32);
let mut wedge = Path::new();
wedge.move_to(center);
wedge.arc_to(inscribed, start_angle, sweep_angle);
wedge.close();
canvas.fill_path(&wedge, color);
}
fn accessibility(&self, builder: &mut AccessNodeBuilder) {
builder.set_role(teksilo_core::accesskit::Role::GenericContainer);
}
}