class PresentationController
Manages the mutable state of a presentation: current slide, clock, and listeners.
Wraps an immutable class Presently::Presentation and provides navigation, timing, and listener notification.
Multiple views (display, presenter) can register as listeners to receive updates.
Definitions
def initialize(presentation, state: nil)
Initialize a new controller for the given presentation.
Signature
-
parameter
presentationPresentation The presentation to control.
-
parameter
stateState | Nil The state persistence object. If provided, state is saved on changes and restored on initialization.
Implementation
def initialize(presentation, state: nil)
@presentation = presentation
@current_index = 0
@clock = Clock.new
@listeners = []
@state = state
@state&.restore(self)
end
attr :presentation
Signature
-
attribute
Presentation The underlying presentation data.
attr :current_index
Signature
-
attribute
Integer The index of the current slide.
attr :clock
Signature
-
attribute
Clock The presentation timer.
def templates
The template resolver, delegated to the presentation.
Signature
-
returns
Templates The templates instance.
Implementation
def templates
@presentation.templates
end
def slides
The ordered list of slides, delegated to the presentation.
Signature
-
returns
Array(Slide) The slides.
Implementation
def slides
@presentation.slides
end
def current_slide
The currently displayed slide.
Signature
-
returns
Slide | Nil The current slide, or
nilif no slides are loaded.
Implementation
def current_slide
@presentation.slides[@current_index]
end
def next_slide
The slide following the current one.
Signature
-
returns
Slide | Nil The next slide, or
nilif on the last slide.
Implementation
def next_slide
@presentation.slides[@current_index + 1]
end
def previous_slide
The slide preceding the current one.
Signature
-
returns
Slide | Nil The previous slide, or
nilif on the first slide.
Implementation
def previous_slide
@presentation.slides[@current_index - 1] if @current_index > 0
end
def slide_count
The total number of slides.
Signature
-
returns
Integer The slide count.
Implementation
def slide_count
@presentation.slide_count
end
def total_duration
The total expected duration of the presentation.
Signature
-
returns
Numeric The total duration in seconds.
Implementation
def total_duration
@presentation.total_duration
end
def slide_progress
The progress through the current slide's allocated time.
Signature
-
returns
Float A value between 0.0 and 1.0.
Implementation
def slide_progress
return 0.0 unless @clock.started?
slide = current_slide
return 0.0 unless slide
time_into_slide = @clock.elapsed - @presentation.expected_time_at(@current_index)
(time_into_slide / slide.duration).clamp(0.0, 1.0)
end
def reset_timer!
Reset the timer so that elapsed time matches the expected time for the current slide.
Implementation
def reset_timer!
@clock.reset!(@presentation.expected_time_at(@current_index))
notify_listeners!
end
def pacing
The current pacing status relative to the slide timing.
Signature
-
returns
Symbol One of
:on_time,:ahead, or:behind.
Implementation
def pacing
return :on_time unless @clock.started?
elapsed = @clock.elapsed
slide_start = @presentation.expected_time_at(@current_index)
slide_end = @presentation.expected_time_at(@current_index + 1)
if elapsed > slide_end
:behind
elsif elapsed < slide_start
:ahead
else
:on_time
end
end
def time_remaining
The estimated time remaining in the presentation.
Signature
-
returns
Numeric The remaining time in seconds.
Implementation
def time_remaining
return total_duration unless @clock.started?
expected_remaining = @presentation.expected_time_at(slide_count) - @clock.elapsed
[expected_remaining, 0].max
end
def go_to(index)
Navigate to a specific slide by index. Ignores out-of-bounds indices. Notifies listeners on change.
Signature
-
parameter
indexInteger The slide index to navigate to.
Implementation
def go_to(index)
return if index < 0 || index >= slide_count
@current_index = index
notify_listeners!
end
def advance!
Advance to the next slide.
Implementation
def advance!
go_to(@current_index + 1)
end
def retreat!
Go back to the previous slide.
Implementation
def retreat!
go_to(@current_index - 1)
end
def add_listener(listener)
Register a listener to be notified when the slide changes.
The listener must respond to #slide_changed!.
Signature
-
parameter
listenerObject The listener to add.
Implementation
def add_listener(listener)
@listeners << listener
end
def remove_listener(listener)
Remove a previously registered listener.
Signature
-
parameter
listenerObject The listener to remove.
Implementation
def remove_listener(listener)
@listeners.delete(listener)
end
def reload!
Reload slides from disk and notify listeners.
Implementation
def reload!
@presentation = @presentation.reload
notify_listeners!
end
def save_state!
Persist the current state to disk.
Implementation
def save_state!
@state&.save(self)
end
def notify_listeners!
Notify all registered listeners that the slide has changed, and persist state.
Implementation
def notify_listeners!
@state&.save(self)
@listeners.each do |listener|
listener.slide_changed!
rescue => error
Console.warn(self, "Listener notification failed", exception: error)
end
end