engine_observables_api/fragment.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Laid-out geometry queries — hit-testing, box-model lookup,
6//! anchor-to-fragments, selection rects.
7//!
8//! Cf. Hekate doc §"Layout/Fragment Plane". The trait is the
9//! permanent ABI; internal plane storage (IndexVec / FxHashMap /
10//! whatever) stays each lane's implementation detail.
11
12use malloc_size_of_derive::MallocSizeOf;
13use serde::{Deserialize, Serialize};
14
15use crate::types::{Point, Rect, SourceNodeId, SourceRange};
16
17/// Common-minimum laid-out-geometry queries. Each lane that has a
18/// layout phase implements this. Internal `FragmentId` type is per-
19/// lane (opaque to consumers — only used as a map key).
20pub trait FragmentQuery {
21 /// Per-lane opaque fragment identity. Consumers compare for
22 /// equality and use as map keys.
23 type FragmentId: Copy + Eq + std::hash::Hash;
24
25 /// Epoch — invalidated on any relayout. Consumers cache against
26 /// this; the value rolls when the plane regenerates.
27 fn generation_id(&self) -> u64;
28
29 /// Hit-test at a viewport point. Returns the topmost fragment
30 /// hit (paint-order semantics), or `None` if the point falls
31 /// outside any fragment.
32 fn hit_test(&self, point: Point) -> Option<FragmentHit<Self::FragmentId>>;
33
34 /// CSS box-model for a source node. None if the node has no
35 /// fragment (e.g., `display: none`, or before layout completes).
36 fn box_model(&self, source_id: SourceNodeId) -> Option<BoxModel>;
37
38 /// Fragments under a named anchor (e.g., `#section-2`).
39 fn fragments_for_anchor<'a>(
40 &'a self,
41 anchor: &str,
42 ) -> Box<dyn Iterator<Item = Self::FragmentId> + 'a>;
43
44 /// Reverse mapping: fragment → source span. Used by selection,
45 /// "what node was here," and Apparatus.
46 fn text_range_for_fragment(&self, fragment: Self::FragmentId) -> Option<SourceRange>;
47
48 /// Selection → screen rects. Multi-rect because a selection can
49 /// span lines or wrap; consumers (selection-highlight painter)
50 /// draw one rect per returned entry.
51 fn rects_for_selection(&self, range: SourceRange) -> Vec<Rect>;
52}
53
54/// What `hit_test` returns. Carries the lane's fragment id, the
55/// containing source node, and the local hit point inside the
56/// fragment (so consumers can drive caret positioning without
57/// re-walking).
58#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
59pub struct FragmentHit<FragmentId: Copy> {
60 pub fragment: FragmentId,
61 pub source_node: SourceNodeId,
62 /// Hit point in the fragment's local space (origin at the
63 /// fragment's top-left).
64 pub local_point: Point,
65}
66
67/// CSS box-model for one source node's fragment. The four nested
68/// rectangles — content / padding / border / margin — are each in
69/// viewport coordinates. Consumers wanting `getBoundingClientRect`
70/// shape read `border` (the border-edge rect).
71#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize)]
72pub struct BoxModel {
73 /// Content-box rect (inside padding).
74 pub content: Rect,
75 /// Padding-box rect (between border and content).
76 pub padding: Rect,
77 /// Border-box rect (between margin and padding). Matches
78 /// `getBoundingClientRect()` in DOM semantics.
79 pub border: Rect,
80 /// Margin-box rect (outermost).
81 pub margin: Rect,
82}