use crate::engine::Context;
pub trait Scene: SceneAsAny + 'static {
fn on_enter(&mut self, _ctx: &mut Context) {}
fn on_exit(&mut self, _ctx: &mut Context) {}
fn on_pause(&mut self, _ctx: &mut Context) {}
fn on_resume(&mut self, _ctx: &mut Context) {}
fn update(&mut self, ctx: &mut Context);
fn render(&mut self, ctx: &mut Context);
fn handle_event(&mut self, _ctx: &mut Context, _event: &SceneEvent) {}
}
#[derive(Debug, Clone)]
pub enum SceneEvent {
WindowResized { width: u32, height: u32 },
GainedFocus,
LostFocus,
Custom(String),
}
#[derive(Debug, Clone)]
pub enum Transition {
None,
Fade { duration: f32, color: [f32; 4] },
Slide { duration: f32, direction: SlideDirection },
}
#[derive(Debug, Clone, Copy)]
pub enum SlideDirection {
Left,
Right,
Up,
Down,
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum TransitionPhase {
Idle,
Out,
In,
}
pub struct SceneManager {
stack: Vec<Box<dyn Scene>>,
transition: Option<TransitionEffect>,
pending_push: Option<Box<dyn Scene>>,
pending_pop: bool,
pending_replace: Option<Box<dyn Scene>>,
}
#[derive(Debug)]
struct TransitionEffect {
#[allow(dead_code)]
kind: Transition,
phase: TransitionPhase,
timer: f32,
half_duration: f32,
}
impl SceneManager {
pub fn new() -> Self {
Self {
stack: Vec::new(),
transition: None,
pending_push: None,
pending_pop: false,
pending_replace: None,
}
}
pub fn push<S: Scene>(&mut self, scene: S) {
match &self.transition {
Some(t) if t.phase != TransitionPhase::Idle => {
self.pending_push = Some(Box::new(scene));
return;
}
_ => {}
}
let mut fake_ctx = self.make_fake_ctx();
if let Some(current) = self.stack.last_mut() {
current.on_pause(&mut fake_ctx);
}
self.stack.push(Box::new(scene));
if let Some(top) = self.stack.last_mut() {
top.on_enter(&mut fake_ctx);
}
}
pub fn pop(&mut self) {
match &self.transition {
Some(t) if t.phase != TransitionPhase::Idle => {
self.pending_pop = true;
return;
}
_ => {}
}
let mut fake_ctx = self.make_fake_ctx();
if let Some(mut scene) = self.stack.pop() {
scene.on_exit(&mut fake_ctx);
}
if let Some(top) = self.stack.last_mut() {
top.on_resume(&mut fake_ctx);
}
}
pub fn replace<S: Scene>(&mut self, scene: S) {
match &self.transition {
Some(t) if t.phase != TransitionPhase::Idle => {
self.pending_replace = Some(Box::new(scene));
return;
}
_ => {}
}
let mut fake_ctx = self.make_fake_ctx();
if let Some(mut old) = self.stack.pop() {
old.on_exit(&mut fake_ctx);
}
self.stack.push(Box::new(scene));
if let Some(top) = self.stack.last_mut() {
top.on_enter(&mut fake_ctx);
}
}
pub fn push_with_transition<S: Scene>(&mut self, scene: S, transition: Transition) {
self.start_transition(transition);
self.pending_push = Some(Box::new(scene));
}
pub fn pop_with_transition(&mut self, transition: Transition) {
self.start_transition(transition);
self.pending_pop = true;
}
pub fn current(&self) -> Option<&dyn Scene> {
self.stack.last().map(|s| s.as_ref())
}
pub fn current_mut(&mut self) -> Option<&mut dyn Scene> {
self.stack.last_mut().map(|s| s.as_mut())
}
pub fn find<S: Scene>(&self) -> Option<&S> {
for scene in &self.stack {
if let Some(s) = scene.as_ref().as_any().downcast_ref::<S>() {
return Some(s);
}
}
None
}
pub fn depth(&self) -> usize {
self.stack.len()
}
pub fn is_empty(&self) -> bool {
self.stack.is_empty()
}
pub fn update(&mut self, ctx: &mut Context) {
let mut trans_opt = self.transition.take();
if let Some(trans) = &mut trans_opt {
trans.timer += ctx.time.delta() as f32;
match trans.phase {
TransitionPhase::Out => {
if trans.timer >= trans.half_duration {
self.execute_pending(ctx);
trans.phase = TransitionPhase::In;
trans.timer = 0.0;
}
}
TransitionPhase::In => {
if trans.timer >= trans.half_duration {
trans_opt = None;
}
}
TransitionPhase::Idle => {}
}
}
self.transition = trans_opt;
if let Some(top) = self.stack.last_mut() {
top.update(ctx);
}
}
pub fn render(&mut self, ctx: &mut Context) {
if let Some(trans) = &self.transition {
match trans.phase {
TransitionPhase::Out => {
if let Some(scene) = self.stack.last_mut() {
scene.render(ctx);
}
self.render_transition_overlay(ctx, trans, false);
}
TransitionPhase::In => {
if let Some(scene) = self.stack.last_mut() {
scene.render(ctx);
}
self.render_transition_overlay(ctx, trans, true);
}
TransitionPhase::Idle => {}
}
} else if let Some(top) = self.stack.last_mut() {
top.render(ctx);
}
}
fn start_transition(&mut self, kind: Transition) {
let half_duration = match &kind {
Transition::None => 0.0,
Transition::Fade { duration, .. } => duration / 2.0,
Transition::Slide { duration, .. } => duration / 2.0,
};
self.transition = Some(TransitionEffect {
kind,
phase: TransitionPhase::Out,
timer: 0.0,
half_duration,
});
}
fn execute_pending(&mut self, ctx: &mut Context) {
if let Some(scene) = self.pending_push.take() {
if let Some(current) = self.stack.last_mut() {
current.on_pause(ctx);
}
self.stack.push(scene);
if let Some(top) = self.stack.last_mut() {
top.on_enter(ctx);
}
}
if self.pending_pop {
self.pending_pop = false;
if let Some(mut scene) = self.stack.pop() {
scene.on_exit(ctx);
}
if let Some(top) = self.stack.last_mut() {
top.on_resume(ctx);
}
}
if let Some(scene) = self.pending_replace.take() {
if let Some(mut old) = self.stack.pop() {
old.on_exit(ctx);
}
self.stack.push(scene);
if let Some(top) = self.stack.last_mut() {
top.on_enter(ctx);
}
}
}
fn render_transition_overlay(&self, _ctx: &mut Context, trans: &TransitionEffect, _is_in: bool) {
let _ = (trans, _is_in); }
fn make_fake_ctx(&self) -> Context {
Context::default()
}
}
pub trait SceneAsAny {
fn as_any(&self) -> &dyn std::any::Any;
}
impl<S: Scene + 'static> SceneAsAny for S {
fn as_any(&self) -> &dyn std::any::Any { self }
}