use std::any::Any;
use std::time::Duration;
use iced_runtime::Task;
use iced_widget::core::widget::{self, Operation};
use iced_widget::core::{Padding, Rectangle};
use crate::ids::Ids;
#[derive(Clone, Debug)]
pub enum FocusTarget<I: Ids> {
All,
Selection,
Node(I::NodeId),
Nodes(Vec<I::NodeId>),
Anchor(I::AnchorId),
Anchors(Vec<I::AnchorId>),
Edge(I::EdgeId),
Edges(Vec<I::EdgeId>),
Rect(Rectangle),
}
#[derive(Clone, Debug)]
pub struct FocusOptions {
pub padding: Padding,
pub min_zoom: Option<f32>,
pub max_zoom: Option<f32>,
pub animation: Option<FocusAnimation>,
}
impl Default for FocusOptions {
fn default() -> Self {
Self {
padding: Padding::new(40.0),
min_zoom: None,
max_zoom: Some(1.0),
animation: Some(FocusAnimation::default()),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct FocusAnimation {
pub duration: Duration,
pub easing: Easing,
}
impl Default for FocusAnimation {
fn default() -> Self {
Self {
duration: Duration::from_millis(300),
easing: Easing::EaseInOutCubic,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Easing {
Linear,
EaseInOutCubic,
EaseOutCubic,
}
impl Easing {
pub fn apply(self, t: f32) -> f32 {
let t = t.clamp(0.0, 1.0);
match self {
Easing::Linear => t,
Easing::EaseInOutCubic => {
if t < 0.5 {
4.0 * t * t * t
} else {
1.0 - (-2.0 * t + 2.0).powi(3) / 2.0
}
}
Easing::EaseOutCubic => 1.0 - (1.0 - t).powi(3),
}
}
}
pub(super) struct FocusRequest<I: Ids> {
pub target: FocusTarget<I>,
pub options: FocusOptions,
}
pub fn focus<I: Ids, T>(
id: impl Into<widget::Id>,
target: FocusTarget<I>,
options: FocusOptions,
) -> Task<T> {
iced_runtime::task::effect(iced_runtime::Action::widget(focus_operation(
id, target, options,
)))
}
pub fn focus_operation<I: Ids>(
id: impl Into<widget::Id>,
target: FocusTarget<I>,
options: FocusOptions,
) -> impl Operation + 'static {
Focus {
id: id.into(),
request: Some(FocusRequest { target, options }),
}
}
struct Focus<I: Ids> {
id: widget::Id,
request: Option<FocusRequest<I>>,
}
impl<I: Ids> Operation for Focus<I> {
fn traverse(&mut self, operate: &mut dyn FnMut(&mut dyn Operation)) {
operate(self);
}
fn custom(&mut self, id: Option<&widget::Id>, _bounds: Rectangle, state: &mut dyn Any) {
if id == Some(&self.id)
&& let Some(slot) = state.downcast_mut::<Option<FocusRequest<I>>>()
{
*slot = self.request.take();
}
}
}