#![allow(non_snake_case)]
use std::rc::Rc;
use crate::composable;
use crate::modifier::Modifier;
use crate::text_selection::{handle_path_data, HandleKind, HANDLE_TOUCH_PADDING};
use crate::widgets::box_widget::{Box, BoxSpec};
use crate::widgets::popup::Popup;
use crate::PointerInputScope;
use cranpose_foundation::PointerEventKind;
use cranpose_ui_graphics::{Brush, Color, DrawScope, Point, Rect, Size, VectorPath};
struct HandleShape {
path_data: String,
box_size: Size,
tip_in_box: Point,
}
fn handle_shape(kind: HandleKind, radius: f32) -> HandleShape {
let pad = HANDLE_TOUCH_PADDING.max(0.0);
let bounds = VectorPath::parse(&handle_path_data(kind, 0.0, 0.0, radius))
.map(|p| p.bounds())
.unwrap_or(Rect {
x: -radius,
y: 0.0,
width: 2.0 * radius,
height: 2.0 * radius,
});
let tip_in_box = Point {
x: pad - bounds.x,
y: pad - bounds.y,
};
let box_size = Size {
width: bounds.width + 2.0 * pad,
height: bounds.height + 2.0 * pad,
};
let path_data = handle_path_data(kind, tip_in_box.x, tip_in_box.y, radius);
HandleShape {
path_data,
box_size,
tip_in_box,
}
}
#[composable]
pub fn SelectionHandle(
kind: HandleKind,
tip: Point,
radius: f32,
color: Color,
on_drag: impl Fn(Point) + 'static,
on_drag_end: impl Fn() + 'static,
) {
let shape = handle_shape(kind, radius);
let anchor = Rect {
x: tip.x - shape.tip_in_box.x,
y: tip.y - shape.tip_in_box.y,
width: 0.0,
height: 0.0,
};
let path_data = shape.path_data;
let box_size = shape.box_size;
let on_drag: Rc<dyn Fn(Point)> = Rc::new(on_drag);
let on_drag_end: Rc<dyn Fn()> = Rc::new(on_drag_end);
Popup(anchor, Point { x: 0.0, y: 0.0 }, move || {
let path_data = path_data.clone();
let on_drag = Rc::clone(&on_drag);
let on_drag_end = Rc::clone(&on_drag_end);
Box(
Modifier::empty()
.size(box_size)
.draw_behind(move |scope: &mut dyn DrawScope| {
if let Ok(path) = VectorPath::parse(&path_data) {
scope.draw_vector_path(&path, Brush::solid(color));
}
})
.pointer_input((), move |scope: PointerInputScope| {
let on_drag = Rc::clone(&on_drag);
let on_drag_end = Rc::clone(&on_drag_end);
async move {
scope
.await_pointer_event_scope(|await_scope| async move {
loop {
let event = await_scope.await_pointer_event().await;
match event.kind {
PointerEventKind::Down | PointerEventKind::Move => {
on_drag(event.global_position);
event.consume();
}
PointerEventKind::Up | PointerEventKind::Cancel => {
on_drag_end();
event.consume();
}
_ => {}
}
}
})
.await;
}
}),
BoxSpec::default(),
|| {},
);
});
}