Integrate scoped reference chooser and scenario form focus and reveal

This commit is contained in:
Codex
2026-09-11 06:24:20 -07:00
parent c7ed894900
commit 7f081ad457
27 changed files with 4544 additions and 79 deletions
+76
View File
@@ -45,6 +45,22 @@ mod scenario_scroll {
area,
process: false,
restore,
reveal: None,
})
}
pub(super) fn observe_canvas<'a, R: renderer::Renderer + 'a>(
context: scenario::Context,
restore: Option<scenario::ScrollRestore>,
reveal: Option<scenario::ScrollReveal>,
content: impl Into<Element<'a, Message, Theme, R>>,
) -> Element<'a, Message, Theme, R> {
Element::new(Observer {
content: content.into(),
context,
area: scenario::ScrollArea::Canvas,
process: false,
restore,
reveal,
})
}
pub(super) fn observe_topology<'a>(
@@ -63,6 +79,7 @@ mod scenario_scroll {
area,
process: true,
restore: None,
reveal: None,
})
}
struct Observer<'a, R = Renderer> {
@@ -71,12 +88,14 @@ mod scenario_scroll {
area: scenario::ScrollArea,
process: bool,
restore: Option<scenario::ScrollRestore>,
reveal: Option<scenario::ScrollReveal>,
}
type Report = Option<(bool, Id, Id, Vector)>;
struct ObservedScroll {
identity: (Id, Id),
report: Report,
restored: Option<Id>,
revealed: Option<(Id, scenario::Slot)>,
}
#[derive(Default)]
struct Translation(Option<Vector>);
@@ -115,6 +134,41 @@ mod scenario_scroll {
self.applied = true;
}
}
struct RevealTranslation {
request: scenario::ScrollReveal,
applied: bool,
}
impl widget::Operation for RevealTranslation {
fn traverse(&mut self, _: &mut dyn FnMut(&mut dyn widget::Operation)) {}
fn scrollable(
&mut self,
_: Option<&widget::Id>,
bounds: Rectangle,
_: Rectangle,
translation: Vector,
state: &mut dyn widget::operation::Scrollable,
) {
let reveal = self.request;
if !reveal.y.is_finite() || !reveal.padding.is_finite() || bounds.height <= 0. {
return;
}
let padding = reveal.padding.max(0.).min(bounds.height / 2.);
let y = if reveal.y - padding < translation.y {
(reveal.y - padding).max(0.)
} else if reveal.y + padding > translation.y + bounds.height {
(reveal.y + padding - bounds.height).max(0.)
} else {
translation.y
};
// Preserve horizontal position and reveal only once per invocation
// and explicit destination. Later ordinary scrolling stays ordinary.
state.scroll_to(widget::operation::scrollable::AbsoluteOffset {
x: None,
y: Some(y),
});
self.applied = true;
}
}
impl<R: renderer::Renderer> Observer<'_, R> {
fn report(
&mut self,
@@ -142,6 +196,27 @@ mod scenario_scroll {
);
operation.applied.then_some(request.token)
});
if let Some(reveal) = self.reveal.filter(|request| {
!self.process
&& receipt.is_none()
&& tree.state.downcast_ref::<ObservedScroll>().revealed
!= Some((request.token, request.slot))
}) {
let mut operation = RevealTranslation {
request: reveal,
applied: false,
};
self.content.as_widget_mut().operate(
&mut tree.children[0],
layout,
renderer,
&mut operation,
);
if operation.applied {
tree.state.downcast_mut::<ObservedScroll>().revealed =
Some((reveal.token, reveal.slot));
}
}
let mut offset = Translation::default();
self.content.as_widget_mut().operate(
&mut tree.children[0],
@@ -202,6 +277,7 @@ mod scenario_scroll {
identity: (self.context.document, self.context.scenario),
report: None,
restored: None,
revealed: None,
})
}
fn children(&self) -> Vec<widget::Tree> {