1use crate::logical_geometry::PhysicalSide;
11use crate::values::computed::{
12 Context, Integer, LengthPercentage, NonNegativeNumber, Percentage, ToComputedValue,
13};
14use crate::values::generics;
15use crate::values::generics::position::{
16 AnchorSideKeyword, AspectRatio as GenericAspectRatio, GenericAnchorFunction, GenericAnchorSide,
17 GenericInset, Position as GenericPosition, PositionComponent as GenericPositionComponent,
18 PositionOrAuto as GenericPositionOrAuto, ZIndex as GenericZIndex,
19};
20pub use crate::values::specified::position::{
21 AnchorName, AnchorScope, DashedIdentAndOrTryTactic, GridAutoFlow, GridTemplateAreas,
22 MasonryAutoFlow, PositionAnchor, PositionArea, PositionAreaAxis, PositionAreaKeyword,
23 PositionAreaType, PositionTryFallbacks, PositionTryFallbacksTryTactic,
24 PositionTryFallbacksTryTacticKeyword, PositionTryOrder, PositionVisibility,
25};
26use crate::Zero;
27use std::fmt::{self, Write};
28use style_traits::{CssWriter, ToCss};
29
30pub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;
32
33pub type PositionOrAuto = GenericPositionOrAuto<Position>;
35
36pub type HorizontalPosition = LengthPercentage;
38
39pub type VerticalPosition = LengthPercentage;
41
42pub type AnchorSide = GenericAnchorSide<Percentage>;
44
45impl AnchorSide {
46 pub fn keyword_and_percentage(&self) -> (AnchorSideKeyword, Percentage) {
48 match self {
49 Self::Percentage(p) => (AnchorSideKeyword::Start, *p),
50 Self::Keyword(k) => {
51 if matches!(k, AnchorSideKeyword::Center) {
52 (AnchorSideKeyword::Start, Percentage(0.5))
53 } else {
54 (*k, Percentage::zero())
55 }
56 },
57 }
58 }
59}
60
61pub type AnchorFunction = GenericAnchorFunction<Percentage, Inset>;
63
64#[cfg(feature = "gecko")]
65use crate::{
66 gecko_bindings::structs::AnchorPosOffsetResolutionParams,
67 values::{computed::Length, DashedIdent},
68};
69
70impl AnchorFunction {
71 #[cfg(feature = "gecko")]
73 pub fn resolve(
74 anchor_name: &DashedIdent,
75 anchor_side: &AnchorSide,
76 prop_side: PhysicalSide,
77 params: &AnchorPosOffsetResolutionParams,
78 ) -> Result<Length, ()> {
79 use crate::gecko_bindings::structs::Gecko_GetAnchorPosOffset;
80
81 let (keyword, percentage) = anchor_side.keyword_and_percentage();
82 let mut offset = Length::zero();
83 let valid = unsafe {
84 Gecko_GetAnchorPosOffset(
85 params,
86 anchor_name.0.as_ptr(),
87 prop_side as u8,
88 keyword as u8,
89 percentage.0,
90 &mut offset,
91 )
92 };
93
94 if !valid {
95 return Err(());
96 }
97
98 Ok(offset)
99 }
100}
101
102pub(crate) trait TryTacticAdjustment {
105 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide);
108}
109
110impl<T: TryTacticAdjustment> TryTacticAdjustment for Box<T> {
111 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
112 (**self).try_tactic_adjustment(old_side, new_side);
113 }
114}
115
116impl<T: TryTacticAdjustment> TryTacticAdjustment for generics::NonNegative<T> {
117 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
118 self.0.try_tactic_adjustment(old_side, new_side);
119 }
120}
121
122impl<Percentage: TryTacticAdjustment> TryTacticAdjustment for GenericAnchorSide<Percentage> {
123 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
124 match self {
125 Self::Percentage(p) => p.try_tactic_adjustment(old_side, new_side),
126 Self::Keyword(side) => side.try_tactic_adjustment(old_side, new_side),
127 }
128 }
129}
130
131impl<Percentage: TryTacticAdjustment, Fallback: TryTacticAdjustment> TryTacticAdjustment
132 for GenericAnchorFunction<Percentage, Fallback>
133{
134 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
135 self.side.try_tactic_adjustment(old_side, new_side);
136 if let Some(fallback) = self.fallback.as_mut() {
137 fallback.try_tactic_adjustment(old_side, new_side);
138 }
139 }
140}
141
142pub type Inset = GenericInset<Percentage, LengthPercentage>;
144impl TryTacticAdjustment for Inset {
145 fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {
153 match self {
154 Self::Auto => {},
155 Self::AnchorContainingCalcFunction(lp) | Self::LengthPercentage(lp) => {
156 lp.try_tactic_adjustment(old_side, new_side)
157 },
158 Self::AnchorFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),
159 Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),
160 }
161 }
162}
163
164impl Position {
165 #[inline]
167 pub fn center() -> Self {
168 Self::new(
169 LengthPercentage::new_percent(Percentage(0.5)),
170 LengthPercentage::new_percent(Percentage(0.5)),
171 )
172 }
173
174 #[inline]
176 pub fn zero() -> Self {
177 Self::new(LengthPercentage::zero(), LengthPercentage::zero())
178 }
179}
180
181impl ToCss for Position {
182 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
183 where
184 W: Write,
185 {
186 self.horizontal.to_css(dest)?;
187 dest.write_char(' ')?;
188 self.vertical.to_css(dest)
189 }
190}
191
192impl GenericPositionComponent for LengthPercentage {
193 fn is_center(&self) -> bool {
194 match self.to_percentage() {
195 Some(Percentage(per)) => per == 0.5,
196 _ => false,
197 }
198 }
199}
200
201#[inline]
202fn block_or_inline_to_inferred(keyword: PositionAreaKeyword) -> PositionAreaKeyword {
203 if matches!(
204 keyword.axis(),
205 PositionAreaAxis::Block | PositionAreaAxis::Inline
206 ) {
207 keyword.with_axis(PositionAreaAxis::Inferred)
208 } else {
209 keyword
210 }
211}
212
213#[inline]
214fn inferred_to_block(keyword: PositionAreaKeyword) -> PositionAreaKeyword {
215 keyword.with_inferred_axis(PositionAreaAxis::Block)
216}
217
218#[inline]
219fn inferred_to_inline(keyword: PositionAreaKeyword) -> PositionAreaKeyword {
220 keyword.with_inferred_axis(PositionAreaAxis::Inline)
221}
222
223impl ToComputedValue for PositionArea {
230 type ComputedValue = Self;
231
232 fn to_computed_value(&self, _context: &Context) -> Self {
233 let mut computed = self.clone();
234 let pair_type = self.get_type();
235 if pair_type == PositionAreaType::Logical || pair_type == PositionAreaType::SelfLogical {
236 if computed.second != PositionAreaKeyword::None {
237 computed.first = block_or_inline_to_inferred(computed.first);
238 computed.second = block_or_inline_to_inferred(computed.second);
239 }
240 } else if pair_type == PositionAreaType::Inferred
241 || pair_type == PositionAreaType::SelfInferred
242 {
243 if computed.second == PositionAreaKeyword::SpanAll {
244 computed.first = inferred_to_block(computed.first);
247 computed.second = PositionAreaKeyword::None;
248 } else if computed.first == PositionAreaKeyword::SpanAll {
249 computed.first = computed.second;
250 computed.first = inferred_to_inline(computed.first);
251 computed.second = PositionAreaKeyword::None;
252 }
253 }
254
255 if computed.first == computed.second {
256 computed.second = PositionAreaKeyword::None;
257 }
258 computed
259 }
260
261 fn from_computed_value(computed: &Self) -> Self {
262 computed.clone()
263 }
264}
265
266pub type ZIndex = GenericZIndex<Integer>;
268
269pub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;