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