Skip to content

Commit

Permalink
Improved router transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmerlin committed May 15, 2024
1 parent 0511cb6 commit 0bfce1b
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 42 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/egui_router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ matchit = "0.8"
egui_inbox = { workspace = true, features = ["type_inbox"] }

eframe = { workspace = true, default-features = true }

egui_animation = { workspace = true }
13 changes: 10 additions & 3 deletions crates/egui_router/examples/router.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use eframe::NativeOptions;
use egui::{CentralPanel, Color32, Context, Frame, Ui, Window};
use egui_inbox::type_inbox::TypeInbox;
use egui_router::{EguiRouter, Request, Route};
use egui_router::{EguiRouter, Request, Route, TransitionConfig};

struct AppState {
message: String,
Expand All @@ -18,13 +18,20 @@ fn main() -> eframe::Result<()> {
let mut router = EguiRouter::new(AppState {
message: "Hello, World!".to_string(),
inbox: TypeInbox::new(ctx.clone()),
});
})
.with_backward_transition(
TransitionConfig::slide().with_easing(egui_animation::easing::quad_in_out),
)
.with_forward_transition(
TransitionConfig::slide().with_easing(egui_animation::easing::quad_in_out),
)
.with_default_duration(0.3);

router.route("/", home);
router.route("/edit", edit_message);
router.route("/post/{id}", post);

router.navigate("/");
router.navigate_transition("/", TransitionConfig::none());

router
};
Expand Down
123 changes: 99 additions & 24 deletions crates/egui_router/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod transition;

use crate::transition::{ActiveTransition, ActiveTransitionResult, Transition, TransitionType};
use egui::emath::ease_in_ease_out;
use egui::Ui;

pub trait Handler<State> {
Expand All @@ -15,19 +16,67 @@ struct RouteState<State> {
route: Box<dyn Route<State>>,
}

struct TransitionConfig {
duration: f32,
#[derive(Debug, Clone)]
pub struct TransitionConfig {
duration: Option<f32>,
easing: fn(f32) -> f32,
_in: Transition,
out: Transition,
}

impl Default for TransitionConfig {
fn default() -> Self {
Self {
duration: None,
easing: ease_in_ease_out,
_in: transition::SlideTransition::new(1.0).into(),
out: transition::SlideTransition::new(-0.1).into(),
}
}
}

impl TransitionConfig {
pub fn new(_in: impl Into<Transition>, out: impl Into<Transition>) -> Self {
Self {
_in: _in.into(),
out: out.into(),
..Self::default()
}
}

pub fn slide() -> Self {
Self::default()
}

pub fn fade() -> Self {
Self::new(transition::FadeTransition, transition::FadeTransition)
}

pub fn none() -> Self {
Self::new(transition::NoTransition, transition::NoTransition)
}

pub fn with_easing(mut self, easing: fn(f32) -> f32) -> Self {
self.easing = easing;
self
}

pub fn with_duration(mut self, duration: f32) -> Self {
self.duration = Some(duration);
self
}
}

pub struct EguiRouter<State> {
router: matchit::Router<Box<dyn Handler<State>>>,
pub state: State,
history: Vec<RouteState<State>>,

forward_transition: TransitionConfig,
backward_transition: TransitionConfig,

current_transition: Option<ActiveTransition>,
default_duration: Option<f32>,
}

pub struct Request<'a, State = ()> {
Expand All @@ -43,16 +92,44 @@ impl<State> EguiRouter<State> {
history: Vec::new(),
// default_transition: transition::Transition::Fade(transition::FadeTransition),
current_transition: None,
forward_transition: TransitionConfig::default(),
backward_transition: TransitionConfig::default(),
default_duration: None,
}
}

pub fn with_transition(mut self, transition: TransitionConfig) -> Self {
self.forward_transition = transition.clone();
self.backward_transition = transition;
self
}

pub fn with_forward_transition(mut self, transition: TransitionConfig) -> Self {
self.forward_transition = transition;
self
}

pub fn with_backward_transition(mut self, transition: TransitionConfig) -> Self {
self.backward_transition = transition;
self
}

pub fn with_default_duration(mut self, duration: f32) -> Self {
self.default_duration = Some(duration);
self
}

pub fn route(&mut self, route: impl Into<String>, handler: impl Handler<State> + 'static) {
self.router
.insert(route.into(), Box::new(handler))
.expect("Invalid route");
}

pub fn navigate(&mut self, route: impl Into<String>) {
pub fn navigate_transition(
&mut self,
route: impl Into<String>,
transition_config: TransitionConfig,
) {
let route = route.into();
let mut handler = self.router.at_mut(&route);

Expand All @@ -63,36 +140,36 @@ impl<State> EguiRouter<State> {
});
self.history.push(RouteState { route });

self.current_transition = Some(ActiveTransition::new(
0.9,
TransitionType::Forward {
_in: transition::Transition::Slide(transition::SlideTransition),
out: transition::Transition::NoTransition(transition::NoTransition),
},
));
self.current_transition = Some(
ActiveTransition::forward(transition_config)
.with_default_duration(self.default_duration),
);
} else {
eprintln!("Failed to navigate to route: {}", route);
}
}

pub fn back(&mut self) {
pub fn back_transition(&mut self, transition_config: TransitionConfig) {
if self.history.len() > 1 {
self.current_transition = Some(ActiveTransition::new(
0.9,
TransitionType::Backward {
_in: transition::Transition::Slide(transition::SlideTransition),
out: transition::Transition::NoTransition(transition::NoTransition),
},
));
self.current_transition = Some(
ActiveTransition::backward(transition_config)
.with_default_duration(self.default_duration),
);
}
}

pub fn navigate(&mut self, route: impl Into<String>) {
self.navigate_transition(route, self.forward_transition.clone());
}

pub fn back(&mut self) {
self.back_transition(self.backward_transition.clone());
}

pub fn ui(&mut self, ui: &mut Ui) {
if let Some((last, previous)) = self.history.split_last_mut() {
let result = if let Some(transition) = &mut self.current_transition {
let transition_result = transition.update(ui.input(|i| i.stable_dt));

transition.show(
Some(transition.show(
ui,
&mut self.state,
|ui, state| {
Expand All @@ -103,9 +180,7 @@ impl<State> EguiRouter<State> {
r.route.ui(ui, state);
}
}),
);

Some(transition_result)
))
} else {
last.route.ui(ui, &mut self.state);
None
Expand Down
Loading

0 comments on commit 0bfce1b

Please sign in to comment.