Godot-Slideshow/Slideshow.gd

196 lines
5.3 KiB
GDScript

extends Control
class_name Slideshow
@onready var button_next := %Next
@onready var button_previous := %Previous
@onready var content := %Content
@onready var frame := %Frame
@export var transition_duration := 0.5
@export var time_per_slide := 5.0
@export var enable_timed_slides := true
@export var enable_slide_inputs := true
var _current_slide_index := 0
var _slide_reset_time := 0.0
var _block_inputs := false
func add_content(new_content: Control) -> void:
content.add_child(new_content)
if content.get_child_count() > 1:
_show_buttons(true)
func _ready() -> void:
_initialize()
func _physics_process(delta: float) -> void:
if enable_timed_slides:
time_per_slide -= delta
# time's up so go to next slide and reset time
if time_per_slide <= 0:
_cycle_next()
time_per_slide = _slide_reset_time
func _initialize() -> void:
button_next.pressed.connect(_cycle_next)
button_previous.pressed.connect(_cycle_previous)
content.gui_input.connect(_handle_input)
# set the temporary variable to hold the value
_slide_reset_time = time_per_slide
# move all slides off screen and fit to content frame (if not ignoring sizes)
_content_fit_size()
_content_reset_positions()
# set the initial slide position if it exists
if content.get_child_count() > 0:
var initial_slide := content.get_child(_current_slide_index)
initial_slide.set_position(content.get_position())
# hide the next/previous buttons if slides <= 1
if content.get_child_count() <= 1:
_show_buttons(false)
func _show_buttons(is_visible: bool) -> void:
button_next.set_visible(is_visible)
button_previous.set_visible(is_visible)
func _content_fit_size() -> void:
for slide in content.get_children():
slide.set_size(content.get_size())
func _content_reset_positions() -> void:
# move all slides off the screen to the left
for slide in content.get_children():
slide.set_position(
Vector2(
frame.get_position().x - frame.get_size().x,
frame.get_position().y
)
)
func _set_to_frame_left(slide: Control) -> void:
slide.set_position(
Vector2(
frame.get_position().x - frame.get_size().x,
slide.get_position().y
)
)
func _set_to_frame_right(slide: Control) -> void:
slide.set_position(
Vector2(
frame.get_position().x + frame.get_size().x,
slide.get_position().y
)
)
func _set_slide() -> void:
# TODO: add a way to go directly to slide in deck
pass
func _cycle_next() -> void:
# if there's only one node or less or inputs are blocked, do nothing
var slide_count := content.get_child_count()
if slide_count <= 1 or _block_inputs:
return
# set variable to block trying to cycle to next/previous slide
_block_inputs = true
# get the index of the next node
var next_index := _current_slide_index + 1
if next_index >= slide_count:
next_index = 0
# set the initial position of next slide to the left of content before moving
var next_slide := content.get_child(next_index)
_set_to_frame_left(next_slide)
# slide the current and next nodes to the right simultaneously
var current_slide := content.get_child(_current_slide_index)
var tween := get_tree().create_tween().set_parallel(true)
tween.tween_property(
current_slide,
"position:x",
frame.get_position().x + frame.get_size().x, # at end of frame
transition_duration
).set_trans(Tween.TRANS_CUBIC)
tween.tween_property(
next_slide,
"position:x",
frame.get_position().x, # target position
transition_duration
).set_trans(Tween.TRANS_CUBIC)
tween.chain().tween_callback(func() -> void: _block_inputs = false)
# set the current index to the next one
_current_slide_index = next_index
time_per_slide = _slide_reset_time
func _cycle_previous() -> void:
# if there's only one node or less or inputs are blocked, do nothing
var slide_count := content.get_child_count()
if slide_count <= 1 or _block_inputs:
return
# set variable to block trying to cycle to next/previous slide
_block_inputs = true
# get the index of the previous node
var previous_index := _current_slide_index - 1
if previous_index < 0:
previous_index = slide_count - 1
# set the initial position of previous slide to the right of content before moving
var previous_slide := content.get_child(previous_index)
_set_to_frame_right(previous_slide)
# slide the current and previous nodes to the left simultaneously
var current_slide := content.get_child(_current_slide_index)
var tween := get_tree().create_tween().set_parallel(true)
tween.tween_property(
current_slide,
"position:x",
frame.get_position().x - frame.get_size().x, # at left of frame
transition_duration
).set_trans(Tween.TRANS_CUBIC)
tween.tween_property(
previous_slide,
"position:x",
frame.get_position().x, # target position
transition_duration
).set_trans(Tween.TRANS_CUBIC)
tween.chain().tween_callback(func() -> void: _block_inputs = false)
# set the current index to the previous one and reset timer
_current_slide_index = previous_index
time_per_slide = _slide_reset_time
func _handle_input(event: InputEvent) -> void:
# handle inputs if enabled
if !enable_slide_inputs:
return
# handle left and mouse clicks from mouse buttons only
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.is_released():
_cycle_next()
if event.button_index == MOUSE_BUTTON_RIGHT and event.is_released():
_cycle_previous()
# TODO: handle events from touch screen