use floating_ui_utils::{
get_padding_object, rect_to_client_rect, Coords, ElementOrVirtual, OwnedElementOrWindow,
Padding, Rect, SideObject,
};
use crate::types::{
Boundary, ConvertOffsetParentRelativeRectToViewportRelativeRectArgs, ElementContext, Elements,
GetClippingRectArgs, MiddlewareState, RootBoundary,
};
#[derive(Clone, Debug)]
pub struct DetectOverflowOptions<Element> {
pub boundary: Option<Boundary<Element>>,
pub root_boundary: Option<RootBoundary>,
pub element_context: Option<ElementContext>,
pub alt_boundary: Option<bool>,
pub padding: Option<Padding>,
}
impl<Element> DetectOverflowOptions<Element> {
pub fn boundary(mut self, value: Boundary<Element>) -> Self {
self.boundary = Some(value);
self
}
pub fn root_boundary(mut self, value: RootBoundary) -> Self {
self.root_boundary = Some(value);
self
}
pub fn element_context(mut self, value: ElementContext) -> Self {
self.element_context = Some(value);
self
}
pub fn alt_boundary(mut self, value: bool) -> Self {
self.alt_boundary = Some(value);
self
}
pub fn padding(mut self, value: Padding) -> Self {
self.padding = Some(value);
self
}
}
impl<Element> Default for DetectOverflowOptions<Element> {
fn default() -> Self {
Self {
boundary: Default::default(),
root_boundary: Default::default(),
element_context: Default::default(),
alt_boundary: Default::default(),
padding: Default::default(),
}
}
}
pub fn detect_overflow<Element: Clone, Window: Clone>(
state: MiddlewareState<Element, Window>,
options: DetectOverflowOptions<Element>,
) -> SideObject {
let MiddlewareState {
x,
y,
platform,
rects,
elements,
strategy,
..
} = state;
let boundary = options.boundary.unwrap_or(Boundary::ClippingAncestors);
let root_boundary = options.root_boundary.unwrap_or(RootBoundary::Viewport);
let element_context = options.element_context.unwrap_or(ElementContext::Floating);
let alt_boundary = options.alt_boundary.unwrap_or(false);
let padding = options.padding.unwrap_or(Padding::All(0.0));
let padding_object = get_padding_object(padding);
let alt_context = match element_context {
ElementContext::Reference => ElementContext::Floating,
ElementContext::Floating => ElementContext::Reference,
};
let element = match alt_boundary {
true => elements.get_element_context(alt_context),
false => elements.get_element_context(element_context),
};
let document_element = platform.get_document_element(elements.floating);
let context_element: Option<Element>;
let element = match element {
ElementOrVirtual::Element(element) => element,
ElementOrVirtual::VirtualElement(virtual_element) => {
context_element = virtual_element.context_element();
context_element
.as_ref()
.or(document_element.as_ref())
.expect("Element should exist.")
}
};
let clipping_client_rect =
rect_to_client_rect(platform.get_clipping_rect(GetClippingRectArgs {
element,
boundary,
root_boundary,
strategy,
}));
let rect = match element_context {
ElementContext::Reference => rects.reference.clone(),
ElementContext::Floating => Rect {
x,
y,
width: rects.floating.width,
height: rects.floating.height,
},
};
let offset_parent = platform.get_offset_parent(elements.floating);
let offset_scale = match offset_parent.as_ref() {
Some(offset_parent) => match offset_parent {
OwnedElementOrWindow::Element(element) => {
platform.get_scale(element).unwrap_or(Coords::new(1.0))
}
OwnedElementOrWindow::Window(_) => Coords::new(1.0),
},
None => Coords::new(1.0),
};
let element_client_rect = rect_to_client_rect(
platform
.convert_offset_parent_relative_rect_to_viewport_relative_rect(
ConvertOffsetParentRelativeRectToViewportRelativeRectArgs {
elements: Some(Elements {
reference: elements.reference,
floating: elements.floating,
}),
rect: rect.clone(),
offset_parent: offset_parent
.as_ref()
.map(|offset_parent| offset_parent.into()),
strategy,
},
)
.unwrap_or(rect),
);
SideObject {
top: (clipping_client_rect.top - element_client_rect.top + padding_object.top)
/ offset_scale.y,
right: (element_client_rect.right - clipping_client_rect.right + padding_object.right)
/ offset_scale.x,
bottom: (element_client_rect.bottom - clipping_client_rect.bottom + padding_object.bottom)
/ offset_scale.y,
left: (clipping_client_rect.left - element_client_rect.left + padding_object.left)
/ offset_scale.x,
}
}