1use std::cell::RefCell;
37
38use super::*;
39use crate::edit::EditOptions;
40use crate::fragment::FragmentRef;
41use crate::line::Stroke;
42use crate::path::{FillRule, PathOp, Turn};
43use crate::resources::{ImageId, ImageOpts};
44use crate::schema::EnvFacts;
45use crate::scroll::ScrollGeometry;
46use crate::slot::Slot;
47use crate::text::Span;
48
49#[derive(Clone, Copy, Debug, PartialEq, Eq)]
53pub enum SlotFill {
54 Replayed,
56 NotKept,
60 Params,
62 Reads,
65 NotReplayable,
69 Moved,
72}
73
74impl SlotFill {
75 pub fn replayed(self) -> bool {
77 self == Self::Replayed
78 }
79
80 pub fn name(self) -> &'static str {
83 match self {
84 Self::Replayed => "replayed",
85 Self::NotKept => "not-kept",
86 Self::Params => "params",
87 Self::Reads => "reads",
88 Self::NotReplayable => "not-replayable",
89 Self::Moved => "moved",
90 }
91 }
92
93 pub fn code(self) -> i32 {
95 self as i32
96 }
97
98 pub fn from_code(code: i32) -> Option<Self> {
100 Some(match code {
101 0 => Self::Replayed,
102 1 => Self::NotKept,
103 2 => Self::Params,
104 3 => Self::Reads,
105 4 => Self::NotReplayable,
106 5 => Self::Moved,
107 _ => return None,
108 })
109 }
110}
111
112pub(crate) enum Op {
114 Open {
116 key: Key,
117 spec: Box<NodeSpec>,
118 },
119 Close,
120 Text {
121 key: Key,
122 content: Box<str>,
123 style: TextStyle,
124 },
125 Rich {
126 key: Key,
127 spans: Vec<(Box<str>, Span<'static>)>,
131 base: TextStyle,
132 },
133 Image {
134 key: Key,
135 id: ImageId,
136 opts: ImageOpts,
137 spec: Box<NodeSpec>,
138 },
139 Fragment {
141 key: Key,
142 frag: FragmentRef,
143 params: Vec<f32>,
144 spec: Box<NodeSpec>,
145 },
146 Line {
147 key: Key,
148 points: Vec<Vec2>,
149 stroke: Stroke,
150 spec: Box<NodeSpec>,
151 },
152 Polygon {
153 key: Key,
154 points: Vec<Vec2>,
155 spec: Box<NodeSpec>,
156 },
157 Path {
158 key: Key,
159 ops: Vec<PathOp>,
160 rule: FillRule,
161 stroke: Option<Stroke>,
162 turn: Option<Turn>,
163 spec: Box<NodeSpec>,
164 },
165 Edit {
166 label: Box<str>,
167 initial: Box<str>,
168 opts: Box<EditOptions>,
169 spec: Box<NodeSpec>,
170 },
171 Label {
173 key: Key,
174 label: Box<str>,
175 },
176 Indexed(u64),
178 RowCount(u64),
180 RowCountOpen(u64),
182 Hint {
183 key: Key,
184 text: Box<str>,
185 },
186 KeyFocus(Key),
187 Slot {
190 name: Box<str>,
191 params: Value,
192 },
193}
194
195#[derive(Debug)]
198pub(crate) enum Read {
199 Hover(Key, bool),
200 Pressed(Key, bool),
201 Drop(Key, bool),
202 GroupHover(u64, bool),
203 GroupPressed(u64, bool),
204 Focus(Option<Key>),
205 Focused(Key, bool),
206 FocusVisible(bool),
207 Caret(bool),
208 Clock,
210 Mods(crate::input::KeyMods),
211 Cursor(Option<Vec2>),
212 Scroll(Key, Vec2),
213 ScrollGeom(Key, Option<ScrollGeometry>),
214 Layout(Key, Option<Rect>),
215 TextHit(Key, Vec2, Option<crate::text::TextHit>),
216 EditText(Key, Option<String>),
217 Env(EnvFacts),
220 Measure(u64),
222 Opaque,
225}
226
227pub(crate) struct Recording {
229 name: String,
230 slot_key: Key,
231 origin: OriginId,
232 params: Value,
233 ops: Vec<Op>,
234 reads: RefCell<Vec<Read>>,
236 taint: Option<&'static str>,
237 first: u32,
239 nodes: u32,
241 foreign: u32,
243 paused: u32,
244 pause_from: u32,
245}
246
247pub(crate) struct Kept {
249 slot_key: Key,
250 origin: OriginId,
251 params: Value,
252 ops: Vec<Op>,
253 reads: Vec<Read>,
254 taint: Option<&'static str>,
255 frame: u64,
258}
259
260impl Kept {
261 pub(crate) fn nodes(&self) -> usize {
263 self.ops
264 .iter()
265 .filter(|op| {
266 matches!(
267 op,
268 Op::Open { .. }
269 | Op::Text { .. }
270 | Op::Rich { .. }
271 | Op::Image { .. }
272 | Op::Fragment { .. }
273 | Op::Line { .. }
274 | Op::Polygon { .. }
275 | Op::Path { .. }
276 | Op::Edit { .. }
277 )
278 })
279 .count()
280 }
281}
282
283fn env_same(a: &EnvFacts, b: &EnvFacts) -> bool {
290 a.env == b.env
291 && a.viewport == b.viewport
292 && a.scale == b.scale
293 && a.focus == b.focus
294 && a.focus_visible == b.focus_visible
295 && a.region == b.region
296}
297
298impl Core {
299 #[inline]
302 pub(crate) fn keeping(&self) -> bool {
303 self.recording.as_ref().is_some_and(|r| r.paused == 0)
304 }
305
306 #[cold]
309 #[inline(never)]
310 pub(crate) fn keep_op(&mut self, op: Op) {
311 let Some(r) = self.recording.as_mut() else {
312 return;
313 };
314 if r.paused > 0 {
315 return;
316 }
317 if matches!(
318 op,
319 Op::Open { .. }
320 | Op::Text { .. }
321 | Op::Rich { .. }
322 | Op::Image { .. }
323 | Op::Fragment { .. }
324 | Op::Line { .. }
325 | Op::Polygon { .. }
326 | Op::Path { .. }
327 | Op::Edit { .. }
328 ) {
329 r.nodes += 1;
330 }
331 r.ops.push(op);
332 }
333
334 #[inline]
336 pub(crate) fn note_read(&self, read: impl FnOnce() -> Read) {
337 if let Some(r) = self.recording.as_ref()
338 && r.paused == 0
339 {
340 r.reads.borrow_mut().push(read());
341 }
342 }
343
344 pub(crate) fn taint_kept(&mut self, why: &'static str) {
348 if let Some(r) = self.recording.as_mut()
349 && r.taint.is_none()
350 {
351 r.taint = Some(why);
352 }
353 }
354
355 pub(crate) fn pause_keeping(&mut self) {
360 let len = self.tree.len() as u32;
361 if let Some(r) = self.recording.as_mut() {
362 if r.paused == 0 {
363 r.pause_from = len;
364 }
365 r.paused += 1;
366 }
367 }
368
369 pub(crate) fn resume_keeping(&mut self) {
370 let len = self.tree.len() as u32;
371 if let Some(r) = self.recording.as_mut()
372 && r.paused > 0
373 {
374 r.paused -= 1;
375 if r.paused == 0 {
376 r.foreign += len.saturating_sub(r.pause_from);
377 }
378 }
379 }
380
381 pub(crate) fn keep_next_fill(&mut self, name: &str, key: Key, params: &Value) {
386 if self.recording.is_some() {
387 return;
388 }
389 self.keep_next = Some((name.to_owned(), key, params.clone()));
390 }
391
392 pub(crate) fn forget_next_fill(&mut self) {
394 self.keep_next = None;
395 }
396
397 pub(crate) fn begin_keeping(&mut self, slot: &Slot<'_>, origin: OriginId) {
400 let Some((name, key, params)) = self.keep_next.take_if(|(_, k, _)| *k == slot.key) else {
401 return;
402 };
403 self.recording = Some(Recording {
404 name,
405 slot_key: key,
406 origin,
407 params,
408 ops: Vec::new(),
409 reads: RefCell::new(Vec::new()),
410 taint: None,
411 first: self.tree.len() as u32,
412 nodes: 0,
413 foreign: 0,
414 paused: 0,
415 pause_from: 0,
416 });
417 }
418
419 pub(crate) fn end_keeping(&mut self, slot: &Slot<'_>) {
422 let Some(r) = self.recording.as_ref() else {
423 return;
424 };
425 if r.slot_key != slot.key {
426 return;
427 }
428 let mut r = self.recording.take().expect("checked");
429 let pushed = (self.tree.len() as u32).saturating_sub(r.first);
430 if r.paused > 0 {
431 r.taint = Some("a pause left open");
434 } else if pushed.saturating_sub(r.foreign) != r.nodes {
435 r.taint = Some("a node pushed through a door the journal does not know");
436 }
437 let kept = Kept {
438 slot_key: r.slot_key,
439 origin: r.origin,
440 params: r.params,
441 ops: r.ops,
442 reads: r.reads.into_inner(),
443 taint: r.taint,
444 frame: self.frame_no,
445 };
446 self.kept.insert(r.name, kept);
447 }
448
449 fn first_moved(&self, reads: &[Read]) -> Option<String> {
452 reads
453 .iter()
454 .find(|r| !self.read_holds(r))
455 .map(|r| format!("{r:?}"))
456 }
457
458 fn read_holds(&self, r: &Read) -> bool {
460 match r {
461 Read::Hover(k, v) => self.interaction.is_hovered(*k) == *v,
462 Read::Pressed(k, v) => self.interaction.is_pressed(*k) == *v,
463 Read::Drop(k, v) => self.interaction.is_drop_target(*k) == *v,
464 Read::GroupHover(g, v) => self.interaction.is_group_hovered(*g) == *v,
465 Read::GroupPressed(g, v) => self.interaction.is_group_pressed(*g) == *v,
466 Read::Focus(k) => self.focus == *k,
467 Read::Focused(k, v) => (self.focus == Some(*k)) == *v,
468 Read::FocusVisible(v) => self.focus_visible == *v,
469 Read::Caret(v) => self.edit.blink_visible() == *v,
470 Read::Clock => false,
471 Read::Mods(m) => self.interaction.modifiers() == *m,
472 Read::Cursor(p) => self.interaction.cursor().map(|c| c.minus(self.dt_shift())) == *p,
473 Read::Scroll(k, v) => self.scroll.offset(*k) == *v,
474 Read::ScrollGeom(k, g) => self.scroll_geometry(*k) == *g,
475 Read::Layout(k, r) => self.layout_of_raw(*k) == *r,
476 Read::TextHit(k, p, h) => self.text_hit_raw(*k, *p) == *h,
477 Read::EditText(k, s) => self.edit.text(*k) == *s,
478 Read::Env(f) => env_same(f, &self.env_facts_raw()),
479 Read::Measure(rev) => self.text_rev() == *rev,
480 Read::Opaque => false,
481 }
482 }
483
484 pub(crate) fn text_rev(&self) -> u64 {
487 self.fonts_rev
488 .wrapping_mul(0x9E37_79B9)
489 .wrapping_add(self.weights_rev)
490 }
491
492 pub(crate) fn take_replayable(
497 &mut self,
498 name: &str,
499 key: Key,
500 params: &Value,
501 ) -> Result<Kept, (SlotFill, Option<String>)> {
502 let Some(kept) = self.kept.remove(name) else {
503 return Err((SlotFill::NotKept, None));
504 };
505 if kept.slot_key != key {
506 return Err((SlotFill::Moved, None));
507 }
508 if let Some(why) = kept.taint {
509 return Err((SlotFill::NotReplayable, Some(why.to_owned())));
510 }
511 if kept.params != *params {
512 return Err((SlotFill::Params, None));
513 }
514 if let Some(moved) = self.first_moved(&kept.reads) {
515 return Err((SlotFill::Reads, Some(moved)));
516 }
517 Ok(kept)
518 }
519
520 pub(crate) fn replay(
524 &mut self,
525 name: &str,
526 slot: &Slot<'_>,
527 mut kept: Kept,
528 filler: Option<&mut dyn crate::slot::Fill>,
529 ) {
530 let origin = kept.origin;
531 let ops = std::mem::take(&mut kept.ops);
532 self.fill_within(slot, origin, filler, |ui| {
533 for op in &ops {
534 replay_op(ui, op);
535 }
536 });
537 kept.ops = ops;
538 kept.frame = self.frame_no;
539 self.kept.insert(name.to_owned(), kept);
540 }
541
542 pub fn slot_fill(&self, name: &str) -> Option<SlotFill> {
546 self.find_slot_fill(name).map(|(f, _)| f)
547 }
548
549 pub fn slot_fill_why(&self, name: &str) -> Option<&str> {
553 self.find_slot_fill(name).and_then(|(_, why)| why)
554 }
555
556 fn find_slot_fill(&self, name: &str) -> Option<(SlotFill, Option<&str>)> {
557 self.slot_fills
559 .iter()
560 .rev()
561 .chain(self.slot_fills_last.iter().rev())
562 .find(|(n, ..)| n == name)
563 .map(|(_, f, w)| (*f, w.as_deref()))
564 }
565
566 pub(crate) fn note_slot_fill(&mut self, name: &str, fill: SlotFill, why: Option<String>) {
567 self.slot_fills.push((name.to_owned(), fill, why));
568 }
569
570 pub fn slot_kept_nodes(&self, name: &str) -> Option<usize> {
572 self.kept.get(name).map(Kept::nodes)
573 }
574
575 pub(crate) fn replay_begin_frame(&mut self) {
577 self.slot_fills_last = std::mem::take(&mut self.slot_fills);
580 self.recording = None;
581 self.keep_next = None;
582 }
583
584 pub(crate) fn replay_finish_frame(&mut self) {
587 let now = self.frame_no;
588 self.kept.retain(|_, k| k.frame == now);
589 }
590}
591
592fn replay_op(ui: &mut Ui<'_>, op: &Op) {
596 let core = ui.core();
597 match op {
598 Op::Open { key, spec } => core.open_with_key(*key, (**spec).clone()),
599 Op::Close => core.close(),
600 Op::Text {
601 key,
602 content,
603 style,
604 } => core.text_with_key(*key, content, *style),
605 Op::Rich { key, spans, base } => {
606 let spans: Vec<Span<'_>> = spans.iter().map(|(text, s)| Span { text, ..*s }).collect();
607 core.rich_text_with_key(*key, &spans, *base);
608 }
609 Op::Image {
610 key,
611 id,
612 opts,
613 spec,
614 } => core.image_with_key(*key, *id, *opts, (**spec).clone()),
615 Op::Fragment {
616 key,
617 frag,
618 params,
619 spec,
620 } => core.fragment_with_key(*key, *frag, params, (**spec).clone()),
621 Op::Line {
622 key,
623 points,
624 stroke,
625 spec,
626 } => core.line_with_key(*key, points, stroke, (**spec).clone()),
627 Op::Polygon { key, points, spec } => {
628 core.polygon_with_key(*key, points, (**spec).clone());
629 }
630 Op::Path {
631 key,
632 ops,
633 rule,
634 stroke,
635 turn,
636 spec,
637 } => core.path_with_key(*key, ops, *rule, *stroke, *turn, (**spec).clone()),
638 Op::Edit {
639 label,
640 initial,
641 opts,
642 spec,
643 } => {
644 core.text_edit(label, initial, opts, (**spec).clone());
645 }
646 Op::Label { key, label } => core.note_label(*key, label),
647 Op::Indexed(i) => {
648 if let Some(at) = core.tree.len().checked_sub(1) {
649 core.tree.indexed.push((at as u32, *i));
650 }
651 }
652 Op::RowCount(n) => {
653 if let Some(at) = core.tree.len().checked_sub(1) {
654 core.tree.row_counts.push((at as u32, *n));
655 }
656 }
657 Op::RowCountOpen(n) => {
658 let at = core.current();
659 core.tree.row_counts.push((at, *n));
660 }
661 Op::Hint { key, text } => core.hint(*key, &**text),
662 Op::KeyFocus(key) => core.set_key_focus(Some(*key)),
663 Op::Slot { name, params } => {
664 ui.slot_with(name, params);
665 }
666 }
667}