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 topMargin() { return _margin.top; }
94 
95     /// Gets the right margin of the space.
96     ptrdiff_t rightMargin() { return _margin.right; }
97 
98     /// Gets the bottom margin of the space.
99     ptrdiff_t bottomMargin() { return _margin.bottom; }
100 
101     /// Gets the left margin of the space.
102     ptrdiff_t leftMargin() { 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 
116   /**
117   * Moves the space to another space.
118   * Params:
119   *   target =  The target of the space.
120   */
121   void moveTo(Location location)(Space target) {
122     assert(target !is null);
123 
124     auto newX = target.x;
125     auto newY = target.y;
126 
127     static if (location == Location.northWest) {
128       newX -= width + target.marginLeft;
129       newY -= height + target.marginTop;
130     }
131     else static if (location == Location.north) {
132       newY -= height + target.marginTop;
133     }
134     else static if (location == Location.northEast) {
135       newX += target.width + target.marginRight;
136       newY -= height + target.marginTop;
137     }
138     else static if (location == Location.east) {
139       newX += target.width + target.marginRight;
140     }
141     else static if (location == Location.southEast) {
142       newX += target.width + target.marginRight;
143       newY += target.height + target.marginBottom;
144     }
145     else static if (location == Location.south) {
146       newY += target.height + target.marginBottom;
147     }
148     else static if (location == Location.southWest) {
149       newX -= width + target.marginLeft;
150       newY += target.height + target.marginBottom;
151     }
152     else static if (location == Location.west) {
153       newX -= width + target.marginLeft;
154     }
155     else {
156       static assert(0);
157     }
158 
159     position = new Point(newX, newY);
160   }
161 
162   /**
163   * Moves the space into another space.
164   * Params:
165   *   target =  The space to move the space into.
166   */
167   void moveIn(Location location)(Space target) {
168     assert(target !is null);
169 
170     auto newX = target.x;
171     auto newY = target.y;
172 
173     static if (location == Location.northWest) {
174       newX += target.paddingLeft;
175       newY += target.paddingTop;
176     }
177     else static if (location == Location.north) {
178       newX += (target.width / 2) - (width / 2);
179       newY += target.paddingTop;
180     }
181     else static if (location == Location.northEast) {
182       newX += target.width - (target.paddingRight + width);
183     }
184     else static if (location == Location.east) {
185       newX += target.width - (target.paddingRight + width);
186       newY += (target.height / 2) - (height / 2);
187     }
188     else static if (location == Location.southEast) {
189       newX += target.width - (target.paddingRight + width);
190       newY += targer.height - (target.paddingBottom + height);
191     }
192     else static if (location == Location.south) {
193       newX += (targer.width / 2) - (width / 2);
194       newY += targer.height - (target.paddingBottom + height);
195     }
196     else static if (location == Location.southWest) {
197       newX += target.paddingLeft;
198       newY += target.height - (target.paddingBottom + height);
199     }
200     else static if (location == Location.west) {
201       newX += target.paddingLeft;
202       newY += (target.height / 2) - (height / 2);
203     }
204     else {
205       static assert(0);
206     }
207 
208     position = new Point(newX, newY);
209   }
210 
211   /**
212   * Centers the x coordinate of the space relative to another space.
213   * Params:
214   *   target =  The target to be relative to.
215   */
216   void centerX(Space target) {
217     position = new Point((target.width / 2) - (width / 2), y);
218   }
219 
220   /**
221   * Centers the y coordinate of the space relative to another space.
222   * Params:
223   *   target =  The target to be relative to.
224   */
225   void centerY(Space target) {
226       position = new Point(x, (target.height / 2) - (height / 2));
227   }
228 
229   /**
230   * Centers the space relative to another space.
231   * Params:
232   *   target =  The target to be relative to.
233   */
234   void center(Space target) {
235     position = new Point((target.width / 2) - (width / 2), (target.height / 2) - (height / 2));
236   }
237 
238   void moveX(ptrdiff_t amount) {
239     position = new Point(x + amount, y);
240   }
241 
242   void moveY(ptrdiff_t amount) {
243     position = new Point(x, y + amount);
244   }
245 
246   /**
247   * Checks whether the space intersects with a point.
248   * Params:
249   *   p = The point to check for intersection with.
250   * Returns:
251   *   If the space intersects with the point.
252   */
253   bool intersect(Point p) {
254 		return (p.x > this.x) &&
255 			(p.x < (this.x + this.width)) &&
256 			(p.y > this.y) &&
257 			(p.y < (this.y + this.height));
258 	}
259 
260   /**
261   * Checks whether the space intersects with another space.
262   * Params:
263   *   target = The space to check for intersection with.
264   * Returns:
265   *   True if the two spaces intersects.
266   */
267   bool intersect(Space target) {
268     return(target.x < this.x + this.width) &&
269       (this.x < (target.x + target.width)) &&
270       (target.y < this.y + this.height) &&
271       (this.y < target.y + target.height);
272    }
273 }