Show: keep web All slides messages small, clear the grid when the popup closes, show it on the end screen (#1866)

This commit is contained in:
FredApps committed 2026-11-05 02:37:37 +01:00
1 parent f49ff09b8a
commit 884e3d3353
5 files changed
+32 -13

No files matched your search

+3 -3
View File
@@ -228,7 +228,7 @@ impl View {
}
});
ui.separator();
if snapshot.grid && !snapshot.ended {
if snapshot.grid {
// All slides opens here, as in the desktop Presenter View; the audience keeps the current slide.
self.grid_ui(ui, snapshot, &mut commands);
return commands;
@@ -303,8 +303,8 @@ impl View {
}
ui.heading("All slides");
ui.label("Choose a slide to show it, or press G to return.");
if snapshot.total > slides.len() && !slides.is_empty() {
ui.label(format!("First {} slides", slides.len()));
if snapshot.total > crate::show::GRID_CAP {
ui.label(format!("First {} slides", crate::show::GRID_CAP));
}
egui::ScrollArea::vertical().show(ui, |ui| {
ui.horizontal_wrapped(|ui| {
+1
View File
@@ -18,6 +18,7 @@ mod presenter;
mod render;
mod ui;
mod zoom;
pub(crate) use zoom::GRID_CAP;
#[cfg(test)]
use media::{on_screen, pointer_on_media_bar, shown_slide_rect, slide_media_bars};
+12 -1
View File
@@ -1133,6 +1133,7 @@ fn remote_presenter_uses_the_audience_state_and_closes_with_the_show() {
closes: usize,
commands: Vec<Command>,
last: Option<Snapshot>,
gone: bool,
}
struct Mock(Rc<RefCell<Bridge>>);
impl Host for Mock {
@@ -1141,7 +1142,7 @@ fn remote_presenter_uses_the_audience_state_and_closes_with_the_show() {
Ok(())
}
fn is_open(&self) -> bool {
true
!self.0.borrow().gone
}
fn commands(&mut self) -> Vec<Command> {
std::mem::take(&mut self.0.borrow_mut().commands)
@@ -1194,11 +1195,21 @@ fn remote_presenter_uses_the_audience_state_and_closes_with_the_show() {
let last = bridge.borrow().last.clone().unwrap();
assert!(last.grid);
assert_eq!(last.slides.as_ref().map(Vec::len), Some(3), "the popup gets a thumbnail per slide");
assert!(last.slides.iter().flatten().all(|p| p.width.max(p.height) <= 96), "thumbnails stay small enough for one message");
assert!(last.current.is_none() && last.next.is_none(), "the grid replaces both previews");
bridge.borrow_mut().commands.push(Command::Goto { slide: 2 });
h.run_steps(2);
let show = h.state().show.as_ref().unwrap();
assert!(!show.presenter_grid && !show.grid);
assert_eq!(show.state.slide, 2);
// Closing the popup with its grid open must not hold auto-advance for the rest of the show.
bridge.borrow_mut().commands.push(Command::Key { name: "G".into(), command: false, shift: false, alt: false, ctrl: false });
h.run_steps(2);
assert!(h.state().show.as_ref().unwrap().presenter_grid);
bridge.borrow_mut().gone = true;
h.run_steps(2);
assert!(!h.state().show.as_ref().unwrap().presenter_grid);
bridge.borrow_mut().gone = false;
bridge.borrow_mut().commands.push(Command::End);
h.run_steps(2);
assert!(h.state().show.is_none());
+15 -8
View File
@@ -80,6 +80,10 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) {
&& !show.reading
&& let Some(host) = app.services.presenter.as_mut()
{
if show.remote_opened && show.presenter_grid && !host.is_open() {
// A grid left open in the closed popup would hold auto-advance for the rest of the show.
show.presenter_grid = false;
}
if !show.remote_opened {
show.remote_opened = true;
if let Err(message) = host.open() {
@@ -157,12 +161,11 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) {
} else if show.remote_grid.len() < grid_indices(doc.slides.len()).len() {
// A few thumbnails per update keep the audience window responsive on the web.
for i in grid_indices(doc.slides.len()).into_iter().skip(show.remote_grid.len()).take(4) {
let size = (160, (160.0 * doc.slide_size.height / doc.slide_size.width.max(1.0)).clamp(1.0, 640.0) as u32);
let image = deckcraft_render::render_slide(
&doc,
i,
&RenderOpts { scale: size.0 as f64 / doc.slide_size.width.max(1.0), size: Some(size), ..Default::default() },
);
// At most 96 px on the long side: 48 thumbnails stay well under the popup's message limit.
let (w, h) = (doc.slide_size.width.max(1.0), doc.slide_size.height.max(1.0));
let scale = REMOTE_THUMB / w.max(h);
let size = (((w * scale).round() as u32).max(1), ((h * scale).round() as u32).max(1));
let image = deckcraft_render::render_slide(&doc, i, &RenderOpts { scale, size: Some(size), ..Default::default() });
let preview = crate::presenter::Preview::from_image(&crate::textures::to_color_image(&image, false));
show.remote_grid.push(preview.unwrap_or(crate::presenter::Preview { width: 1, height: 1, rgba: vec![0; 4] }));
}
@@ -178,8 +181,9 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) {
blank: show.blank_name().map(str::to_owned),
grid: show.presenter_grid,
zoom_armed: show.zoom_arm,
current: show.remote_preview.clone(),
next: show.remote_next.as_ref().filter(|(n, _)| Some(*n) == next).map(|(_, image)| image.clone()),
// The grid replaces both previews in the popup; leaving them out keeps one message small.
current: show.remote_preview.clone().filter(|_| !show.presenter_grid),
next: show.remote_next.as_ref().filter(|(n, _)| Some(*n) == next && !show.presenter_grid).map(|(_, image)| image.clone()),
slides: show.presenter_grid.then(|| show.remote_grid.clone()),
});
show.remote_sent = now;
@@ -189,6 +193,9 @@ pub fn ui(app: &mut SlideApp, ui: &mut Ui) {
finish(app, show, &f, out.keep);
}
/// Longest side of an All slides thumbnail sent to the web presenter window (#1866).
const REMOTE_THUMB: f64 = 96.0;
fn paint_hidden(ui: &Ui, doc: &Presentation, show: &Show) {
if show.started {
let full = ui.max_rect();
+1 -1
View File
@@ -182,7 +182,7 @@ const WHEEL_NOTCHES_PER_DOUBLING: f32 = 4.0;
/// PowerPoint's slide show magnifier goes up to 400%.
const WHEEL_ZOOM_MAX: f32 = 4.0;
pub(super) const GRID_CAP: usize = 48;
pub(crate) const GRID_CAP: usize = 48;
const TOOL_BAR_W: f32 = 520.0;