mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-31 05:56:55 -06:00
Merge PR #520 from NikitkaRa: Insert: Summary, Section and Slide Zoom with Return to Zoom
Fix-ups in the merge (review): zoom targets, sections and titles are looked up with get() instead of indexing; Hyperlink literals carry both main's sound fields and zoom_return; duplicated test statements left by the test-file union are removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+27
-1
@@ -4,7 +4,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use deckcraft_model::Presentation;
|
||||
use deckcraft_model::text::Action;
|
||||
use deckcraft_model::text::{Action, Hyperlink};
|
||||
|
||||
use crate::Timeline;
|
||||
use crate::interact::SlideRun;
|
||||
@@ -78,6 +78,9 @@ pub struct ShowState {
|
||||
order: Option<Vec<usize>>,
|
||||
/// Name of the custom show being played, when there is one.
|
||||
custom: Option<String>,
|
||||
/// Return to Zoom: (zoom slide, last zoomed slide). Advancing past the last zoomed slide goes
|
||||
/// back to the zoom slide, fully built.
|
||||
pub zoom: Option<(usize, usize)>,
|
||||
}
|
||||
|
||||
/// Slide indices the show visits in order: the custom show or range from Set Up Slide Show, minus
|
||||
@@ -252,6 +255,16 @@ impl ShowState {
|
||||
self.playing = true;
|
||||
return ShowAction::PlayStep(self.step);
|
||||
}
|
||||
if let Some((back, until)) = self.zoom
|
||||
&& until == self.slide
|
||||
&& back < pres.slides.len()
|
||||
{
|
||||
self.zoom = None;
|
||||
self.enter(pres, back);
|
||||
self.step = ShowState::steps(pres, back);
|
||||
self.playing = false;
|
||||
return ShowAction::Slide(back);
|
||||
}
|
||||
match self.next_index(pres) {
|
||||
Some(i) => {
|
||||
self.enter(pres, i);
|
||||
@@ -331,6 +344,7 @@ impl ShowState {
|
||||
if i >= pres.slides.len() {
|
||||
return ShowAction::None;
|
||||
}
|
||||
self.zoom = None;
|
||||
self.enter(pres, i);
|
||||
ShowAction::Slide(i)
|
||||
}
|
||||
@@ -363,6 +377,18 @@ impl ShowState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Follow a clicked link, honouring a zoom's Return to Zoom (see [`Hyperlink::zoom_return`]).
|
||||
pub fn follow_link(&mut self, pres: &Presentation, link: &Hyperlink, last_viewed: Option<usize>) -> LinkJump {
|
||||
let from = self.slide;
|
||||
let jump = self.follow(pres, &link.action, last_viewed);
|
||||
if let Some(until) = link.zoom_return.and_then(|id| pres.slides.iter().position(|s| s.id == id))
|
||||
&& matches!(jump, LinkJump::Show(ShowAction::Slide(_)))
|
||||
{
|
||||
self.zoom = Some((from, until));
|
||||
}
|
||||
jump
|
||||
}
|
||||
|
||||
/// Seconds after which the current slide advances by itself (Advance Slide ▸ After), when the
|
||||
/// show uses timings.
|
||||
pub fn advance_after(&self, pres: &Presentation) -> Option<f64> {
|
||||
|
||||
@@ -996,3 +996,35 @@ fn media_in_the_click_sequence_cues_and_does_not_hold_the_click() {
|
||||
let tl = Timeline::new(&slide_with(vec![play, anim(2, AnimClass::Entrance, "fade", AnimStart::AfterPrevious)]), W, H);
|
||||
assert!((tl.step_duration(0) - 6.0).abs() < 1e-9, "{}", tl.step_duration(0));
|
||||
}
|
||||
|
||||
/// Zoom with Return to Zoom: after the zoomed slides the show goes back to the zoom slide, fully
|
||||
/// built; without it, or after jumping elsewhere, the show carries on in order.
|
||||
#[test]
|
||||
fn show_returns_to_the_zoom_slide() {
|
||||
use deckcraft_model::text::{Action, Hyperlink};
|
||||
let p = pres(6);
|
||||
let link = |to: u32, last: Option<u32>| Hyperlink {
|
||||
action: Action::Slide { slide: SlideId(1000 + to) },
|
||||
tooltip: String::new(),
|
||||
highlight_click: false,
|
||||
zoom_return: last.map(|l| SlideId(1000 + l)),
|
||||
sound: None,
|
||||
stop_sound: false,
|
||||
};
|
||||
let mut s = ShowState::new(&p);
|
||||
// A section zoom on slide 0 for slides 2–3.
|
||||
assert_eq!(s.follow_link(&p, &link(2, Some(3)), None), LinkJump::Show(ShowAction::Slide(2)));
|
||||
assert_eq!(s.next(&p), ShowAction::Slide(3));
|
||||
assert_eq!(s.next(&p), ShowAction::Slide(0), "back to the zoom");
|
||||
assert_eq!(s.step, ShowState::steps(&p, 0));
|
||||
assert_eq!(s.next(&p), ShowAction::Slide(1), "then on in order");
|
||||
// Without Return to Zoom the show carries on.
|
||||
let mut s = ShowState::new(&p);
|
||||
s.follow_link(&p, &link(4, None), None);
|
||||
assert_eq!(s.next(&p), ShowAction::Slide(5));
|
||||
// Jumping elsewhere (See All Slides, a number) forgets the zoom.
|
||||
let mut s = ShowState::new(&p);
|
||||
s.follow_link(&p, &link(2, Some(2)), None);
|
||||
s.goto(&p, 4);
|
||||
assert_eq!(s.next(&p), ShowAction::Slide(5));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user