floating_ui_core/
detect_overflow.rs1use floating_ui_utils::{
2 Coords, ElementOrVirtual, OwnedElementOrWindow, Padding, Rect, SideObject, get_padding_object,
3 rect_to_client_rect,
4};
5
6use crate::types::{
7 Boundary, ConvertOffsetParentRelativeRectToViewportRelativeRectArgs, ElementContext, Elements,
8 GetClippingRectArgs, MiddlewareState, RootBoundary,
9};
10
11#[derive(Clone, Debug, PartialEq)]
13pub struct DetectOverflowOptions<Element> {
14 pub boundary: Option<Boundary<Element>>,
18
19 pub root_boundary: Option<RootBoundary>,
23
24 pub element_context: Option<ElementContext>,
28
29 pub alt_boundary: Option<bool>,
33
34 pub padding: Option<Padding>,
38}
39
40impl<Element> DetectOverflowOptions<Element> {
41 pub fn boundary(mut self, value: Boundary<Element>) -> Self {
43 self.boundary = Some(value);
44 self
45 }
46
47 pub fn root_boundary(mut self, value: RootBoundary) -> Self {
49 self.root_boundary = Some(value);
50 self
51 }
52
53 pub fn element_context(mut self, value: ElementContext) -> Self {
55 self.element_context = Some(value);
56 self
57 }
58
59 pub fn alt_boundary(mut self, value: bool) -> Self {
61 self.alt_boundary = Some(value);
62 self
63 }
64
65 pub fn padding(mut self, value: Padding) -> Self {
67 self.padding = Some(value);
68 self
69 }
70}
71
72impl<Element> Default for DetectOverflowOptions<Element> {
73 fn default() -> Self {
74 Self {
75 boundary: Default::default(),
76 root_boundary: Default::default(),
77 element_context: Default::default(),
78 alt_boundary: Default::default(),
79 padding: Default::default(),
80 }
81 }
82}
83
84pub fn detect_overflow<Element: Clone + 'static, Window: Clone + 'static>(
91 state: MiddlewareState<Element, Window>,
92 options: DetectOverflowOptions<Element>,
93) -> SideObject {
94 let MiddlewareState {
95 x,
96 y,
97 platform,
98 rects,
99 elements,
100 strategy,
101 ..
102 } = state;
103
104 let boundary = options.boundary.unwrap_or(Boundary::ClippingAncestors);
105 let root_boundary = options.root_boundary.unwrap_or(RootBoundary::Viewport);
106 let element_context = options.element_context.unwrap_or(ElementContext::Floating);
107 let alt_boundary = options.alt_boundary.unwrap_or(false);
108 let padding = options.padding.unwrap_or(Padding::All(0.0));
109
110 let padding_object = get_padding_object(padding);
111 let alt_context = match element_context {
112 ElementContext::Reference => ElementContext::Floating,
113 ElementContext::Floating => ElementContext::Reference,
114 };
115 let element = if alt_boundary {
116 elements.get_element_context(alt_context)
117 } else {
118 elements.get_element_context(element_context)
119 };
120
121 let document_element = platform.get_document_element(elements.floating);
122 let context_element: Option<Element>;
123
124 let element = match element {
125 ElementOrVirtual::Element(element) => element,
126 ElementOrVirtual::VirtualElement(virtual_element) => {
127 context_element = virtual_element.context_element();
128
129 context_element
130 .as_ref()
131 .or(document_element.as_ref())
132 .expect("Element should exist.")
133 }
134 };
135
136 let clipping_client_rect =
137 rect_to_client_rect(platform.get_clipping_rect(GetClippingRectArgs {
138 element,
139 boundary,
140 root_boundary,
141 strategy,
142 }));
143
144 let rect = match element_context {
145 ElementContext::Reference => rects.reference.clone(),
146 ElementContext::Floating => Rect {
147 x,
148 y,
149 width: rects.floating.width,
150 height: rects.floating.height,
151 },
152 };
153
154 let offset_parent = platform.get_offset_parent(elements.floating);
155 let offset_scale = match offset_parent.as_ref() {
156 Some(offset_parent) => match offset_parent {
157 OwnedElementOrWindow::Element(element) => {
158 platform.get_scale(element).unwrap_or(Coords::new(1.0))
159 }
160 OwnedElementOrWindow::Window(_) => Coords::new(1.0),
161 },
162 None => Coords::new(1.0),
163 };
164
165 let element_client_rect = rect_to_client_rect(
166 platform
167 .convert_offset_parent_relative_rect_to_viewport_relative_rect(
168 ConvertOffsetParentRelativeRectToViewportRelativeRectArgs {
169 elements: Some(Elements {
170 reference: elements.reference,
171 floating: elements.floating,
172 }),
173 rect: rect.clone(),
174 offset_parent: offset_parent
175 .as_ref()
176 .map(|offset_parent| offset_parent.into()),
177 strategy,
178 },
179 )
180 .unwrap_or(rect),
181 );
182
183 SideObject {
184 top: (clipping_client_rect.top - element_client_rect.top + padding_object.top)
185 / offset_scale.y,
186 right: (element_client_rect.right - clipping_client_rect.right + padding_object.right)
187 / offset_scale.x,
188 bottom: (element_client_rect.bottom - clipping_client_rect.bottom + padding_object.bottom)
189 / offset_scale.y,
190 left: (clipping_client_rect.left - element_client_rect.left + padding_object.left)
191 / offset_scale.x,
192 }
193}