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