use pathfinder_geometry::rect::RectF; use pathfinder_geometry::vector::{vec2f, Vector2F}; use super::{ AfterLayoutContext, AppContext, Element, EventContext, LayoutContext, PaintContext, Point, SelectableElement, Selection, SelectionFragment, SizeConstraint, }; use crate::event::DispatchedEvent; use crate::text::word_boundaries::WordBoundariesPolicy; use crate::text::{IsRect, SelectionDirection, SelectionType}; pub struct Align { child: Box, alignment: Vector2F, size: Option, } /// By default, Align centers a child element impl Align { pub fn new(child: Box) -> Self { Self { child, alignment: Vector2F::zero(), size: None, } } pub fn top_center(mut self) -> Self { self.alignment = vec2f(0.0, -1.0); self } pub fn top_right(mut self) -> Self { self.alignment = vec2f(1.0, -1.0); self } pub fn top_left(mut self) -> Self { self.alignment = vec2f(-1., -1.); self } pub fn bottom_center(mut self) -> Self { self.alignment = vec2f(0.0, 1.0); self } pub fn bottom_right(mut self) -> Self { self.alignment = vec2f(1.0, 1.0); self } pub fn bottom_left(mut self) -> Self { self.alignment = vec2f(-1., 1.0); self } pub fn right(mut self) -> Self { self.alignment = vec2f(1.0, 0.); self } pub fn left(mut self) -> Self { self.alignment = vec2f(-1., 0.); self } } impl Element for Align { fn layout( &mut self, constraint: SizeConstraint, ctx: &mut LayoutContext, app: &AppContext, ) -> Vector2F { let mut size = constraint.max; let child_constraint = SizeConstraint::new(Vector2F::zero(), constraint.max); let child_size = self.child.layout(child_constraint, ctx, app); if size.x().is_infinite() { size.set_x(child_size.x().max(constraint.min.x())); } if size.y().is_infinite() { size.set_y(child_size.y().max(constraint.min.y())); } self.size = Some(size); size } fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &AppContext) { self.child.after_layout(ctx, app); } fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) { let self_center = self.size.unwrap() / 2.0; let self_target = self_center + self_center * self.alignment; let child_center = self.child.size().unwrap() / 2.0; let mut child_target = child_center + child_center * self.alignment; // Make sure the child_target cannot extend past self which may happen if child size is // larger than self size. child_target = child_target.min(self_target); let child_origin = origin - (child_target - self_target); self.child.paint(child_origin, ctx, app); } fn dispatch_event( &mut self, event: &DispatchedEvent, ctx: &mut EventContext, app: &AppContext, ) -> bool { self.child.dispatch_event(event, ctx, app) } fn size(&self) -> Option { self.size } fn origin(&self) -> Option { self.child.origin() } fn as_selectable_element(&self) -> Option<&dyn SelectableElement> { Some(self as &dyn SelectableElement) } #[cfg(any(test, feature = "test-util"))] fn debug_text_content(&self) -> Option { self.child.debug_text_content() } } impl SelectableElement for Align { fn get_selection( &self, selection_start: Vector2F, selection_end: Vector2F, is_rect: IsRect, ) -> Option> { self.child .as_selectable_element() .and_then(|selectable_child| { selectable_child.get_selection(selection_start, selection_end, is_rect) }) } fn expand_selection( &self, point: Vector2F, direction: SelectionDirection, unit: SelectionType, word_boundaries_policy: &WordBoundariesPolicy, ) -> Option { self.child .as_selectable_element() .and_then(|selectable_child| { selectable_child.expand_selection(point, direction, unit, word_boundaries_policy) }) } fn is_point_semantically_before( &self, absolute_point: Vector2F, absolute_point_other: Vector2F, ) -> Option { self.child .as_selectable_element() .and_then(|selectable_child| { selectable_child.is_point_semantically_before(absolute_point, absolute_point_other) }) } fn smart_select( &self, absolute_point: Vector2F, smart_select_fn: crate::elements::SmartSelectFn, ) -> Option<(Vector2F, Vector2F)> { self.child .as_selectable_element() .and_then(|selectable_child| { selectable_child.smart_select(absolute_point, smart_select_fn) }) } fn calculate_clickable_bounds(&self, current_selection: Option) -> Vec { self.child .as_selectable_element() .map(|selectable_child| selectable_child.calculate_clickable_bounds(current_selection)) .unwrap_or_default() } }