use crate::TransitionConfig;
use egui::{Id, Ui, UiBuilder, Vec2};
pub trait TransitionTrait {
fn create_child_ui(&self, ui: &mut Ui, t: f32, with_id: Id) -> Ui;
}
pub trait ComposableTransitionTrait {
fn apply(&self, ui: &mut Ui, t: f32);
}
impl<T: ComposableTransitionTrait> TransitionTrait for T {
fn create_child_ui(&self, ui: &mut Ui, t: f32, with_id: Id) -> Ui {
let mut child = ui.new_child(UiBuilder::new().max_rect(ui.max_rect()).id_salt(with_id));
self.apply(&mut child, t);
child
}
}
#[derive(Debug, Clone)]
pub enum Transition {
Fade(FadeTransition),
NoTransition(NoTransition),
Slide(SlideTransition),
SlideFade(SlideFadeTransition),
}
impl TransitionTrait for Transition {
fn create_child_ui(&self, ui: &mut Ui, t: f32, with_id: Id) -> Ui {
match self {
Transition::Fade(fade) => fade.create_child_ui(ui, t, with_id),
Transition::NoTransition(no_transition) => {
no_transition.create_child_ui(ui, t, with_id)
}
Transition::Slide(slide) => slide.create_child_ui(ui, t, with_id),
Transition::SlideFade(slide_fade) => slide_fade.create_child_ui(ui, t, with_id),
}
}
}
#[derive(Debug, Clone)]
pub struct FadeTransition;
#[derive(Debug, Clone)]
pub struct NoTransition;
#[derive(Debug, Clone)]
pub struct SlideTransition {
pub amount: Vec2,
}
#[derive(Debug, Clone)]
pub struct SlideFadeTransition(pub SlideTransition, pub FadeTransition);
impl Default for SlideTransition {
fn default() -> Self {
Self { amount: Vec2::X }
}
}
impl SlideTransition {
pub fn new(amount: Vec2) -> Self {
Self { amount }
}
}
impl ComposableTransitionTrait for FadeTransition {
fn apply(&self, ui: &mut Ui, t: f32) {
ui.set_opacity(t);
}
}
impl ComposableTransitionTrait for NoTransition {
fn apply(&self, _: &mut Ui, _: f32) {}
}
impl TransitionTrait for SlideTransition {
fn create_child_ui(&self, ui: &mut Ui, t: f32, with_id: Id) -> Ui {
let available_size = ui.available_size();
let offset = available_size * (1.0 - t) * self.amount;
let offset = Vec2::new(offset.x.round(), offset.y.round());
let child_rect = ui.max_rect().translate(offset);
ui.new_child(UiBuilder::new().max_rect(child_rect).id_salt(with_id))
}
}
impl TransitionTrait for SlideFadeTransition {
fn create_child_ui(&self, ui: &mut Ui, t: f32, with_id: Id) -> Ui {
let mut child = self.0.create_child_ui(ui, t, with_id);
self.1.apply(&mut child, t);
child
}
}
impl From<FadeTransition> for Transition {
fn from(fade: FadeTransition) -> Self {
Transition::Fade(fade)
}
}
impl From<NoTransition> for Transition {
fn from(no_transition: NoTransition) -> Self {
Transition::NoTransition(no_transition)
}
}
impl From<SlideTransition> for Transition {
fn from(slide: SlideTransition) -> Self {
Transition::Slide(slide)
}
}
impl From<SlideFadeTransition> for Transition {
fn from(slide_fade: SlideFadeTransition) -> Self {
Transition::SlideFade(slide_fade)
}
}
pub enum TransitionType {
Forward {
in_: Transition,
out: Transition,
},
Backward {
in_: Transition,
out: Transition,
},
}
pub(crate) struct ActiveTransition {
duration: Option<f32>,
progress: f32,
easing: fn(f32) -> f32,
in_: Transition,
out: Transition,
backward: bool,
manual_control: bool,
}
pub(crate) enum ActiveTransitionResult {
Done,
Continue,
}
impl ActiveTransition {
pub fn forward(config: TransitionConfig) -> Self {
Self {
duration: config.duration,
easing: config.easing,
progress: 0.0,
in_: config.in_,
out: config.out,
backward: false,
manual_control: false,
}
}
pub fn backward(config: TransitionConfig) -> Self {
Self {
duration: config.duration,
easing: config.easing,
progress: 0.0,
in_: config.in_,
out: config.out,
backward: true,
manual_control: false,
}
}
pub fn manual(config: TransitionConfig) -> Self {
Self {
duration: config.duration,
easing: config.easing,
progress: 0.0,
in_: config.in_,
out: config.out,
backward: false,
manual_control: true,
}
}
pub fn progress(&self) -> f32 {
self.progress
}
pub fn is_backward(&self) -> bool {
self.backward
}
pub fn set_progress(&mut self, progress: f32) {
self.progress = progress.clamp(0.0, 1.0);
}
pub fn with_default_duration(mut self, duration: Option<f32>) -> Self {
if self.duration.is_none() {
self.duration = duration;
}
self
}
pub fn show<State>(
&mut self,
ui: &mut Ui,
state: &mut State,
(in_id, content_in): (usize, impl FnOnce(&mut Ui, &mut State)),
content_out: Option<(usize, impl FnOnce(&mut Ui, &mut State))>,
) -> ActiveTransitionResult {
if !self.manual_control {
let dt = ui.input(|i| i.stable_dt);
self.progress += dt / self.duration.unwrap_or_else(|| ui.style().animation_time);
}
let t = self.progress.min(1.0);
ui.ctx().request_repaint();
let eased_t = if self.manual_control {
t
} else {
(self.easing)(t)
};
let eased_t_rev = if self.manual_control {
1.0 - t
} else {
(self.easing)(1.0 - t)
};
if self.backward {
with_temp_auto_id(ui, in_id, |ui| {
let mut out_ui =
self.out
.create_child_ui(ui, eased_t, Id::new("router_child").with(in_id));
content_in(&mut out_ui, state);
});
if let Some((out_id, content_out)) = content_out {
with_temp_auto_id(ui, out_id, |ui| {
let mut in_ui = self.in_.create_child_ui(
ui,
eased_t_rev,
Id::new("router_child").with(out_id),
);
content_out(&mut in_ui, state);
});
}
} else {
if let Some((out_id, content_out)) = content_out {
with_temp_auto_id(ui, out_id, |ui| {
let mut out_ui = self.out.create_child_ui(
ui,
eased_t_rev,
Id::new("router_child").with(out_id),
);
content_out(&mut out_ui, state);
});
}
with_temp_auto_id(ui, in_id, |ui| {
let mut in_ui =
self.in_
.create_child_ui(ui, eased_t, Id::new("router_child").with(in_id));
content_in(&mut in_ui, state);
});
}
if self.progress >= 1.0 && !self.manual_control {
ActiveTransitionResult::Done
} else {
ActiveTransitionResult::Continue
}
}
pub fn show_default(ui: &mut Ui, with_id: usize, content: impl FnOnce(&mut Ui)) {
with_temp_auto_id(ui, with_id, |ui| {
let mut ui = ui.new_child(
UiBuilder::new()
.max_rect(ui.max_rect())
.id_salt(Id::new("router_child").with(with_id)),
);
content(&mut ui);
});
}
}
fn with_temp_auto_id(ui: &mut Ui, id: usize, content: impl FnOnce(&mut Ui)) {
ui.skip_ahead_auto_ids(id);
content(ui);
ui.skip_ahead_auto_ids(usize::MAX - (id));
}