1 // Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9
10 //! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
11 //! and margins in CSS.
12
13 use length::Length;
14 use num::Zero;
15 use core::fmt;
16 use core::ops::{Add, Neg};
17 use core::marker::PhantomData;
18 use core::cmp::{Eq, PartialEq};
19 use core::hash::{Hash};
20 #[cfg(feature = "serde")]
21 use serde::{Deserialize, Serialize};
22 use crate::Vector2D;
23
24 /// A group of 2D side offsets, which correspond to top/right/bottom/left for borders, padding,
25 /// and margins in CSS, optionally tagged with a unit.
26 #[repr(C)]
27 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28 #[cfg_attr(feature = "serde", serde(bound(serialize = "T: Serialize", deserialize = "T: Deserialize<'de>")))]
29 pub struct SideOffsets2D<T, U> {
30 pub top: T,
31 pub right: T,
32 pub bottom: T,
33 pub left: T,
34 #[doc(hidden)]
35 pub _unit: PhantomData<U>,
36 }
37
38 impl<T: Copy, U> Copy for SideOffsets2D<T, U> {}
39
40 impl<T: Clone, U> Clone for SideOffsets2D<T, U> {
clone(&self) -> Self41 fn clone(&self) -> Self {
42 SideOffsets2D {
43 top: self.top.clone(),
44 right: self.right.clone(),
45 bottom: self.bottom.clone(),
46 left: self.left.clone(),
47 _unit: PhantomData,
48 }
49 }
50 }
51
52 impl<T, U> Eq for SideOffsets2D<T, U> where T: Eq {}
53
54 impl<T, U> PartialEq for SideOffsets2D<T, U>
55 where T: PartialEq
56 {
eq(&self, other: &Self) -> bool57 fn eq(&self, other: &Self) -> bool {
58 self.top == other.top &&
59 self.right == other.right &&
60 self.bottom == other.bottom &&
61 self.left == other.left
62 }
63 }
64
65 impl<T, U> Hash for SideOffsets2D<T, U>
66 where T: Hash
67 {
hash<H: ::core::hash::Hasher>(&self, h: &mut H)68 fn hash<H: ::core::hash::Hasher>(&self, h: &mut H) {
69 self.top.hash(h);
70 self.right.hash(h);
71 self.bottom.hash(h);
72 self.left.hash(h);
73 }
74 }
75
76 impl<T: fmt::Debug, U> fmt::Debug for SideOffsets2D<T, U> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result77 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78 write!(
79 f,
80 "({:?},{:?},{:?},{:?})",
81 self.top, self.right, self.bottom, self.left
82 )
83 }
84 }
85
86 impl<T: Default, U> Default for SideOffsets2D<T, U> {
default() -> Self87 fn default() -> Self {
88 SideOffsets2D {
89 top: Default::default(),
90 right: Default::default(),
91 bottom: Default::default(),
92 left: Default::default(),
93 _unit: PhantomData,
94 }
95 }
96 }
97
98 impl<T, U> SideOffsets2D<T, U> {
99 /// Constructor taking a scalar for each side.
100 ///
101 /// Sides are specified in top-right-bottom-left order following
102 /// CSS's convention.
new(top: T, right: T, bottom: T, left: T) -> Self103 pub const fn new(top: T, right: T, bottom: T, left: T) -> Self {
104 SideOffsets2D {
105 top,
106 right,
107 bottom,
108 left,
109 _unit: PhantomData,
110 }
111 }
112
113 /// Constructor taking a typed Length for each side.
114 ///
115 /// Sides are specified in top-right-bottom-left order following
116 /// CSS's convention.
from_lengths( top: Length<T, U>, right: Length<T, U>, bottom: Length<T, U>, left: Length<T, U>, ) -> Self117 pub fn from_lengths(
118 top: Length<T, U>,
119 right: Length<T, U>,
120 bottom: Length<T, U>,
121 left: Length<T, U>,
122 ) -> Self {
123 SideOffsets2D::new(top.0, right.0, bottom.0, left.0)
124 }
125
126 /// Construct side offsets from min and a max vector offsets.
127 ///
128 /// The outer rect of the resulting side offsets is equivalent to translating
129 /// a rectangle's upper-left corner with the min vector and translating the
130 /// bottom-right corner with the max vector.
from_vectors_outer(min: Vector2D<T, U>, max: Vector2D<T,U>) -> Self where T: Neg<Output = T>131 pub fn from_vectors_outer(min: Vector2D<T, U>, max: Vector2D<T,U>) -> Self
132 where
133 T: Neg<Output = T>
134 {
135 SideOffsets2D {
136 left: -min.x,
137 top: -min.y,
138 right: max.x,
139 bottom: max.y,
140 _unit: PhantomData,
141 }
142 }
143
144 /// Construct side offsets from min and a max vector offsets.
145 ///
146 /// The inner rect of the resulting side offsets is equivalent to translating
147 /// a rectangle's upper-left corner with the min vector and translating the
148 /// bottom-right corner with the max vector.
from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T,U>) -> Self where T: Neg<Output = T>149 pub fn from_vectors_inner(min: Vector2D<T, U>, max: Vector2D<T,U>) -> Self
150 where
151 T: Neg<Output = T>
152 {
153 SideOffsets2D {
154 left: min.x,
155 top: min.y,
156 right: -max.x,
157 bottom: -max.y,
158 _unit: PhantomData,
159 }
160 }
161 }
162
163 impl<T: Copy, U> SideOffsets2D<T, U> {
164 /// Constructor setting the same value to all sides, taking a scalar value directly.
new_all_same(all: T) -> Self165 pub fn new_all_same(all: T) -> Self {
166 SideOffsets2D::new(all, all, all, all)
167 }
168
169 /// Constructor setting the same value to all sides, taking a typed Length.
from_length_all_same(all: Length<T, U>) -> Self170 pub fn from_length_all_same(all: Length<T, U>) -> Self {
171 SideOffsets2D::new_all_same(all.0)
172 }
173 }
174
175 impl<T, U> SideOffsets2D<T, U>
176 where
177 T: Add<T, Output = T> + Copy,
178 {
horizontal(&self) -> T179 pub fn horizontal(&self) -> T {
180 self.left + self.right
181 }
182
vertical(&self) -> T183 pub fn vertical(&self) -> T {
184 self.top + self.bottom
185 }
186 }
187
188 impl<T, U> Add for SideOffsets2D<T, U>
189 where
190 T: Add<T, Output = T>,
191 {
192 type Output = Self;
add(self, other: Self) -> Self193 fn add(self, other: Self) -> Self {
194 SideOffsets2D::new(
195 self.top + other.top,
196 self.right + other.right,
197 self.bottom + other.bottom,
198 self.left + other.left,
199 )
200 }
201 }
202
203 impl<T: Zero, U> SideOffsets2D<T, U> {
204 /// Constructor, setting all sides to zero.
zero() -> Self205 pub fn zero() -> Self {
206 SideOffsets2D::new(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero())
207 }
208 }
209
210 #[test]
from_vectors()211 fn from_vectors() {
212 use crate::{vec2, point2};
213 type Box2D = crate::default::Box2D<i32>;
214
215 let b = Box2D {
216 min: point2(10, 10),
217 max: point2(20, 20),
218 };
219
220 let outer = b.outer_box(SideOffsets2D::from_vectors_outer(vec2(-1, -2), vec2(3, 4)));
221 let inner = b.inner_box(SideOffsets2D::from_vectors_inner(vec2(1, 2), vec2(-3, -4)));
222
223 assert_eq!(outer, Box2D { min: point2(9, 8), max: point2(23, 24) });
224 assert_eq!(inner, Box2D { min: point2(11, 12), max: point2(17, 16) });
225 }
226