ElyShow: a deck fits the slide once; a zoom then stays (S-03); per-deck state moves to extra.rs (S-02)

This commit is contained in:
ZacharyZhang-NY committed 2026-10-09 10:39:45 -04:00
1 parent f9bf82e3f8
commit ab85673eea
6 files changed
+105 -57

No files matched your search

+12 -55
View File
@@ -1,16 +1,15 @@
//! ElyShow's window: decks over one LOK worker (§10.1).
use std::{
collections::{HashMap, HashSet},
collections::HashSet,
path::PathBuf,
sync::{Arc, Mutex},
};
use ely_gpui_component::{feedback::EmptyState, primitives::IconName, shell::StatusBar};
use ely_lok::{
LokView, Sheets, Worker,
history::Step,
show::{Design, Objects},
LokView, Worker,
show::Design,
};
use ely_office::{Core, Kind, prelude::*};
use ely_platform::AppId;
@@ -18,61 +17,13 @@ use ely_ui::{Root, Shell, commands};
use gpui::prelude::FluentBuilder as _;
use gpui::{
AnyElement, App, AppContext as _, Context, Entity, InteractiveElement as _, IntoElement,
ParentElement as _, Render, RenderImage, SharedString, Styled as _, Window, div,
ParentElement as _, Render, SharedString, Styled as _, Window, div,
};
use crate::design::Panel;
use crate::slides::Strip;
/// Parts, edits, caret and selected frame.
pub type Seen = (u64, u64, bool, Option<ely_lok::Rect>);
/// What ElyShow keeps per deck.
#[derive(Default)]
pub struct Extra {
pub slides: Sheets,
/// The document as the last read saw it.
pub read_at: Option<ely_lok::Doc>,
/// What the view showed at the last read.
pub read: Option<Seen>,
pub reading: bool,
pub design: Option<Design>,
pub objects: Option<Objects>,
pub notes: Option<ely_lok::show::Notes>,
pub remarks: Vec<ely_lok::show::Remark>,
/// The shown slide's transition and effects; none on a master.
pub motion: Option<(ely_lok::show::Transition, Vec<ely_lok::show::Effect>)>,
pub media: Vec<ely_lok::show::Media>,
/// Counts completed reads.
pub reads: u64,
/// The slide size in twips.
pub size: (i64, i64),
/// Undo and redo lists at the last read.
pub history: Option<(Vec<Step>, Vec<Step>)>,
/// A size sent, in 1/100 mm, until read back.
pub size_asked: Option<(i64, i64)>,
/// A transition sent for a slide, until read back.
pub transition_asked: Option<(String, ely_lok::show::Transition, u64)>,
/// Pictures by slide name; stale ones show until repainted.
pub thumbs: HashMap<String, Arc<RenderImage>>,
pub retired: ely_gpui_component::primitives::Retired,
pub stale: HashSet<String>,
/// Pictures that failed; retried after the next read.
pub failed: HashSet<String>,
pub painting: bool,
/// The parts are masters.
pub masters: bool,
/// The scheme the pictures were painted with.
pub dark: Option<bool>,
}
impl Extra {
/// The size to show, in 1/100 mm: asked, else read.
pub fn shown_size(&self) -> (i64, i64) {
self.size_asked
.unwrap_or_else(|| crate::design::mm100(self.size))
}
}
pub use crate::extra::Extra;
pub struct Deck {
pub core: Core<Deck>,
@@ -137,7 +88,13 @@ impl Host for Deck {
}
fn observed(&mut self, id: u64, view: &Entity<LokView>, cx: &mut Context<Self>) {
view.update(cx, |view, cx| view.fit_page(cx));
// A new deck fits once; a zoom then stays.
if let Some(tab) = self.tab_mut(id)
&& !tab.extra.fitted
{
tab.extra.fitted = true;
view.update(cx, |view, cx| view.fit_page(cx));
}
// Pictures follow the kernel's light or dark scheme.
let dark = view.read(cx).dark();
if let Some(tab) = self.tab_mut(id)
+65
View File
@@ -0,0 +1,65 @@
//! What ElyShow keeps per deck.
use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
use ely_lok::{
Sheets,
history::Step,
show::{Design, Objects},
};
use gpui::RenderImage;
/// Parts, edits, caret and selected frame.
pub type Seen = (u64, u64, bool, Option<ely_lok::Rect>);
/// What ElyShow keeps per deck.
#[derive(Default)]
pub struct Extra {
pub slides: Sheets,
/// The document as the last read saw it.
pub read_at: Option<ely_lok::Doc>,
/// What the view showed at the last read.
pub read: Option<Seen>,
pub reading: bool,
pub design: Option<Design>,
pub objects: Option<Objects>,
pub notes: Option<ely_lok::show::Notes>,
pub remarks: Vec<ely_lok::show::Remark>,
/// The shown slide's transition and effects; none on a master.
pub motion: Option<(ely_lok::show::Transition, Vec<ely_lok::show::Effect>)>,
pub media: Vec<ely_lok::show::Media>,
/// Counts completed reads.
pub reads: u64,
/// The slide size in twips.
pub size: (i64, i64),
/// Undo and redo lists at the last read.
pub history: Option<(Vec<Step>, Vec<Step>)>,
/// A size sent, in 1/100 mm, until read back.
pub size_asked: Option<(i64, i64)>,
/// A transition sent for a slide, until read back.
pub transition_asked: Option<(String, ely_lok::show::Transition, u64)>,
/// Pictures by slide name; stale ones show until repainted.
pub thumbs: HashMap<String, Arc<RenderImage>>,
pub retired: ely_gpui_component::primitives::Retired,
pub stale: HashSet<String>,
/// Pictures that failed; retried after the next read.
pub failed: HashSet<String>,
pub painting: bool,
/// The parts are masters.
pub masters: bool,
/// The scheme the pictures were painted with.
pub dark: Option<bool>,
/// The view was fitted to the slide once.
pub fitted: bool,
}
impl Extra {
/// The size to show, in 1/100 mm: asked, else read.
pub fn shown_size(&self) -> (i64, i64) {
self.size_asked
.unwrap_or_else(|| crate::design::mm100(self.size))
}
}
+1
View File
@@ -3,6 +3,7 @@ mod check;
mod deck;
mod design;
mod dialog;
mod extra;
mod guides;
mod insert;
mod list;
+24
View File
@@ -384,3 +384,27 @@ fn a_comment_picked_on_another_slide_shows_its_slide_again() {
settle(&cx, |app| current(app) == Some(0));
assert_eq!(cx.update(|app| deck.read(app).shown_remark()), Some(key));
}
#[test]
fn a_zoom_stays_through_later_reports() {
use ely_gpui_component::shell::ZoomStep;
let dir = tempfile::tempdir().expect("a temp dir");
let mut cx = TestAppContext::single();
let Some(deck) = blank(&mut cx, dir.path()) else {
eprintln!("SKIP: no LOK build");
return;
};
let view = |app: &gpui::App| deck.read(app).active_tab().expect("a tab").view.clone();
settle(&cx, |app| view(app).read(app).fitting());
update(&mut cx, &deck, |deck, cx| deck.zoom(ZoomStep::In, cx));
let zoom = cx.update(|app| view(app).read(app).zoom());
update(&mut cx, &deck, |deck, cx| {
let (id, view) = deck.active_tab().map(|tab| (tab.id, tab.view.clone())).expect("a tab");
deck.observed(id, &view, cx);
});
cx.update(|app| {
let view = view(app);
assert!(!view.read(app).fitting(), "a report does not fit the slide again");
assert_eq!(view.read(app).zoom(), zoom, "the zoom stays");
});
}
+2 -2
View File
@@ -24,8 +24,8 @@
- [ ] T-14 导入扫描把整个文件的每个字段都建成字符串
- [ ] T-15 非 UTF-8 文本的默认编码跟系统区域而不是界面语言
- [ ] S-01 切换幻灯片后主画布不重画
- [ ] S-02 `deck.rs` 超过 500 行
- [ ] S-03 ElyShow 的缩放一两帧后弹回“适合幻灯片”,缩放功能不可用
- [x] S-02 `deck.rs` 超过 500 行 — 随 S-03 把 Extra 移到 extra.rs,deck.rs 463 行
- [x] S-03 ElyShow 的缩放一两帧后弹回“适合幻灯片”,缩放功能不可用 — 只在标签首次观察时适合一次;R1 PASS
- [ ] S-04 选屏对话框被别的对话框顶掉后,`show_starting` 卡住、放映副本泄漏
- [ ] S-05 一条批注的时间恰为午夜或只有日期,整个批注栏不显示
- [ ] S-06 导出或打印的“从–到”填 0 时导出全部幻灯片
+1
View File
@@ -0,0 +1 @@
PASS