class_name Show extends Node2D # Manage slide cycling signal end_reached const VOID_SLIDE_ID := -1 @export_group("Slide management") @export var start_slide := 0 @export var hide_slides := false @export_group("Input setup", "action_") @export var action_finish_slide := "ui_accept" @export var action_skip_slide := "ui_right" @export var action_previous_slide := "ui_left" @export var action_home_slide := "ui_cancel" @export_group("Export") @export var one_slide_per_frame := true @export var quit_after_last_slide := true var current_id := VOID_SLIDE_ID var current_slide: Slide @onready var focus: Focus = $"../Focus" @onready var slides: Array = get_children() @onready var movie_enabled: bool = OS.has_feature("movie") @onready var auto_next_slide: bool = movie_enabled and one_slide_per_frame func _ready() -> void: _enforce() print("The show contains %s slides" % slides.size()) if hide_slides: _hide_every_slide() _global_slide_setup() if auto_next_slide: end_reached.connect(get_tree().quit) return go_slowly_to_next_slide() func _process(_delta: float) -> void: if auto_next_slide: go_fast_to_next_slide() func go_slowly_to_next_slide() -> void: next_slide(Focus.Transit.SMOOTH) func go_fast_to_next_slide() -> void: next_slide(Focus.Transit.BEAM) func go_fast_to_previous_slide() -> void: next_slide_at(_previous_id(), Focus.Transit.BEAM) func next_slide(trans: Focus.Transit) -> void: next_slide_at(_next_id(), trans) func next_slide_at(next_id: int, trans: Focus.Transit) -> void: _disable_current_slide() current_id = next_id current_slide = _focus_slide(current_id, trans) if is_last_slide(): end_reached.emit() _enable_current_slide() func is_last_slide() -> bool: return current_id == slides.size() - 1 func _focus_slide(id: int, trans: Focus.Transit) -> Slide: var slide: Slide = slides[id] focus.focus_on(slide.get_center(), slide.get_scale().x, trans) return slide func _hide_every_slide() -> void: for slide in slides: slide.gently_hide() func _global_slide_setup() -> void: for slide in slides: slide.set_action_finish(action_finish_slide) func _disable_current_slide() -> void: if current_slide != null: current_slide.disable() current_slide.finished.disconnect(_on_current_slide_finished) func _enable_current_slide() -> void: current_slide.enable() current_slide.finished.connect(_on_current_slide_finished) func _enforce() -> void: _enforce_parameters() _enforce_children() func _enforce_parameters() -> void: assert(focus, "A Focus node must be next to the Show node") assert(start_slide >= 0, "Negative index is not supported for start_slide") assert(start_slide < slides.size(), "start_slide index is out of bound (max is %s)" % (slides.size() - 1)) func _enforce_children() -> void: assert(!slides.is_empty(), "A slideshow must contain slides") for object in slides: assert(object is Slide, "A slideshow can only contain slides but encontered %s" % object) func _next_id() -> int: if current_id == VOID_SLIDE_ID: return start_slide elif is_last_slide(): return 0 return current_id + 1 func _previous_id() -> int: var next_id: int = current_id - 1 if next_id < 0: return slides.size() - 1 return next_id func _reset_id() -> void: current_id = VOID_SLIDE_ID func _unhandled_key_input(event: InputEvent) -> void: if !event.is_pressed(): return if event.is_action(action_skip_slide): get_viewport().set_input_as_handled() go_fast_to_next_slide() elif event.is_action(action_previous_slide): get_viewport().set_input_as_handled() go_fast_to_previous_slide() elif event.is_action(action_home_slide): get_viewport().set_input_as_handled() _reset_id() go_slowly_to_next_slide() func _unhandled_input(event: InputEvent) -> void: if event is InputEventMouseButton and event.is_pressed(): _focus_nearest_slide_from(get_global_mouse_position()) func _focus_nearest_slide_from(target_pos: Vector2) -> void: var next_slide = _search_nearest_slide_from(target_pos) var next_id = slides.find(next_slide) next_slide_at(next_id, Focus.Transit.SMOOTH) func _search_nearest_slide_from(target_pos: Vector2) -> Slide: var nearest_slide = current_slide var shortest_distance = target_pos.distance_squared_to(nearest_slide.get_center()) for current_slide in slides: var current_distance = target_pos.distance_squared_to(current_slide.get_center()) if current_distance < shortest_distance: nearest_slide = current_slide shortest_distance = current_distance return nearest_slide func _on_current_slide_finished() -> void: go_slowly_to_next_slide()