1 /**
2 * Module for space and dimension manipulation.
3 *
4 * Authors:
5 *   Jacob Jensen
6 * License:
7 *   https://github.com/PoisonEngine/poison-ui/blob/master/LICENSE
8 */
9 module poison.ui.space;
10 
11 import poison.core : Point, Size, Edge, Location, EventObserver, ChangeEventArgs;
12 
13 /// A wrapper around a space.
14 class Space : EventObserver {
15   private:
16   /// The position.
17   Point _position;
18 
19   /// The size.
20   Size _size;
21 
22   /// The margin.
23   Edge _margin;
24 
25   /// The padding.
26   Edge _padding;
27 
28   public:
29   /**
30   * Creates a new space.
31   * Params:
32   *   position =  The position.
33   *   size =      The size.
34   */
35   this(Point position, Size size) {
36     assert(position !is null);
37     assert(size !is null);
38 
39     _position = position;
40     _size = size;
41 
42     _margin = new Edge(0,0,0,0);
43     _padding = new Edge(0,0,0,0);
44   }
45 
46   @property {
47     /// Gets the position of the space.
48     Point position() { return _position; }
49 
50     /// Sets the position of the space.
51     void position(Point newPosition) {
52       auto oldPosition = _position;
53       _position = newPosition;
54 
55       fireEvent("position", new ChangeEventArgs!Point(oldPosition, _position));
56     }
57 
58     /// Gets the x coordinate of the space.
59     ptrdiff_t x() { return _position.x; }
60 
61     /// Gets the y coordinate of the space.
62     ptrdiff_t y() { return _position.y; }
63 
64     /// Gets the size of the space.
65     Size size() { return _size; }
66 
67     /// Sets the size of the space.
68     void size(Size newSize) {
69       auto oldSize = _size;
70       _size = newSize;
71 
72       fireEvent("size", new ChangeEventArgs!Size(oldSize, _size));
73     }
74 
75     /// Gets the width of the space.
76     size_t width() { return _size.width; }
77 
78     /// Gets the height of the space.
79     size_t height() { return _size.height; }
80 
81     /// Gets the margin of the space.
82     Edge margin() { return _margin; }
83 
84     /// Sets the margin of the space.
85     void margin(Edge newMargin) {
86       auto oldMargin = _margin;
87       _margin = newMargin;
88 
89       fireEvent("margin", new ChangeEventArgs!Edge(oldMargin, _margin));
90     }
91 
92     /// Gets the top margin of the space.
93     ptrdiff_t marginTop() { return _margin.top; }
94 
95     /// Gets the right margin of the space.
96     ptrdiff_t marginRight() { return _margin.right; }
97 
98     /// Gets the bottom margin of the space.
99     ptrdiff_t marginBottom() { return _margin.bottom; }
100 
101     /// Gets the left margin of the space.
102     ptrdiff_t marginLeft() { return _margin.left; }
103 
104     /// Gets the padding of the space.
105     Edge padding() { return _padding; }
106 
107     /// Sets the padding of the space.
108     void padding(Edge newPadding) {
109       auto oldPadding = _padding;
110       _padding = newPadding;
111 
112       fireEvent("padding", new ChangeEventArgs!Edge(oldPadding, _padding));
113     }
114 
115     /// Gets the top padding of the space.
116     ptrdiff_t paddingTop() { return _padding.top; }
117 
118     /// Gets the right padding of the space.
119     ptrdiff_t paddingRight() { return _padding.right; }
120 
121     /// Gets the bottom padding of the space.
122     ptrdiff_t paddingBottom() { return _padding.bottom; }
123 
124     /// Gets the left padding of the space.
125     ptrdiff_t paddingLeft() { return _padding.left; }
126   }
127 
128   /**
129   * Moves the space to another space.
130   * Params:
131   *   target =  The target of the space.
132   */
133   void moveTo(Location location)(Space target) {
134     assert(target !is null);
135 
136     auto newX = target.x;
137     auto newY = target.y;
138 
139     static if (location == Location.northWest) {
140       newX -= width + target.marginLeft;
141       newY -= height + target.marginTop;
142     }
143     else static if (location == Location.north) {
144       newX += (target.width / 2) - (width / 2);
145       newY -= height + target.marginTop;
146     }
147     else static if (location == Location.northEast) {
148       newX += target.width + target.marginRight;
149       newY -= height + target.marginTop;
150     }
151     else static if (location == Location.east) {
152       newX += target.width + target.marginRight;
153       newY += (target.height / 2) - (height / 2);
154     }
155     else static if (location == Location.southEast) {
156       newX += target.width + target.marginRight;
157       newY += target.height + target.marginBottom;
158     }
159     else static if (location == Location.south) {
160       newX += (target.width / 2) - (width / 2);
161       newY += target.height + target.marginBottom;
162     }
163     else static if (location == Location.southWest) {
164       newX -= width + target.marginLeft;
165       newY += target.height + target.marginBottom;
166     }
167     else static if (location == Location.west) {
168       newX -= width + target.marginLeft;
169       newY += (target.height / 2) - (height / 2);
170     }
171     else {
172       static assert(0);
173     }
174 
175     position = new Point(newX, newY);
176   }
177 
178   /**
179   * Moves the space into another space.
180   * Params:
181   *   target =  The space to move the space into.
182   */
183   void moveIn(Location location)(Space target) {
184     assert(target !is null);
185 
186     auto newX = target.x;
187     auto newY = target.y;
188 
189     static if (location == Location.northWest) {
190       newX += target.paddingLeft;
191       newY += target.paddingTop;
192     }
193     else static if (location == Location.north) {
194       newX += (target.width / 2) - (width / 2);
195       newY += target.paddingTop;
196     }
197     else static if (location == Location.northEast) {
198       newX += target.width - (target.paddingRight + width);
199       newY += target.paddingTop;
200     }
201     else static if (location == Location.east) {
202       newX += target.width - (target.paddingRight + width);
203       newY += (target.height / 2) - (height / 2);
204     }
205     else static if (location == Location.southEast) {
206       newX += target.width - (target.paddingRight + width);
207       newY += target.height - (target.paddingBottom + height);
208     }
209     else static if (location == Location.south) {
210       newX += (target.width / 2) - (width / 2);
211       newY += target.height - (target.paddingBottom + height);
212     }
213     else static if (location == Location.southWest) {
214       newX += target.paddingLeft;
215       newY += target.height - (target.paddingBottom + height);
216     }
217     else static if (location == Location.west) {
218       newX += target.paddingLeft;
219       newY += (target.height / 2) - (height / 2);
220     }
221     else {
222       static assert(0);
223     }
224 
225     position = new Point(newX, newY);
226   }
227 
228   /**
229   * Centers the x coordinate of the space relative to another space.
230   * Params:
231   *   target =  The target to be relative to.
232   */
233   void centerX(Space target) {
234     position = new Point((target.width / 2) - (width / 2), y);
235   }
236 
237   /**
238   * Centers the y coordinate of the space relative to another space.
239   * Params:
240   *   target =  The target to be relative to.
241   */
242   void centerY(Space target) {
243       position = new Point(x, (target.height / 2) - (height / 2));
244   }
245 
246   /**
247   * Centers the space relative to another space.
248   * Params:
249   *   target =  The target to be relative to.
250   */
251   void center(Space target) {
252     position = new Point((target.width / 2) - (width / 2), (target.height / 2) - (height / 2));
253   }
254 
255   void moveX(ptrdiff_t amount) {
256     position = new Point(x + amount, y);
257   }
258 
259   void moveY(ptrdiff_t amount) {
260     position = new Point(x, y + amount);
261   }
262 
263   /**
264   * Checks whether the space intersects with a point.
265   * Params:
266   *   p = The point to check for intersection with.
267   * Returns:
268   *   If the space intersects with the point.
269   */
270   bool intersect(Point p) {
271 		return (p.x > this.x) &&
272 			(p.x < (this.x + cast(ptrdiff_t)this.width)) &&
273 			(p.y > this.y) &&
274 			(p.y < (this.y + cast(ptrdiff_t)this.height));
275 	}
276 
277   /**
278   * Checks whether the space intersects with another space.
279   * Params:
280   *   target = The space to check for intersection with.
281   * Returns:
282   *   True if the two spaces intersects.
283   */
284   bool intersect(Space target) {
285     return(target.x < this.x + cast(ptrdiff_t)this.width) &&
286       (this.x < (target.x + cast(ptrdiff_t)target.width)) &&
287       (target.y < this.y + cast(ptrdiff_t)this.height) &&
288       (this.y < target.y + cast(ptrdiff_t)target.height);
289    }
290 }