1 module poison.ui.graphics;
2 
3 import dsfml.graphics : RectangleShape;
4 import dsfml.system : Vector2f;
5 
6 import poison.ui.paint;
7 import poison.core : Size, Point;
8 import poison.ui.picture;
9 import poison.ui.fonts;
10 
11 /// Original space struct for graphical elements.
12 private struct OriginalSpace(TPosition,TSize) {
13   /// The original x position.
14   TPosition x;
15 
16   /// The original y position.
17   TPosition y;
18 
19   /// The original width.
20   TSize width;
21 
22   /// The original height.
23   TSize height;
24 }
25 
26 /// A sub rect of overflow calculation results.
27 private struct SubRect {
28   /// The x coordinate.
29   float x;
30 
31   /// The y coordinate.
32   float y;
33 
34   /// The width.
35   float width;
36 
37   /// The height.
38   float height;
39 
40   /// The overflow top.
41   float overflowTop;
42 
43   /// The overflow right.
44   float overflowRight;
45 
46   /// The overflow bottom.
47   float overflowBottom;
48 
49   /// The overflow left.
50   float overflowLeft;
51 }
52 
53 /// Graphics wrapper for components.
54 class Graphics {
55   private:
56   /// The background paint.
57   Paint _backgroundPaint;
58 
59   /// The foreground paint.
60   Paint _foregroundPaint;
61 
62   /// The low-level background rectangle.
63   RectangleShape _backgroundRect;
64 
65   /// The original background rectangle's space.
66   OriginalSpace!(float,float) _originalBackgroundRect;
67 
68   /// The background picture.
69   Picture _backgroundPicture;
70 
71   /// The original background ´picture's space.
72   OriginalSpace!(ptrdiff_t,int) _originalBackgroundPicture;
73 
74   /// The size.
75   Size _size;
76 
77   /// The position.
78   Point _position;
79 
80   /// The font.
81   Font _font;
82 
83   /// The font size.
84   uint _fontSize;
85 
86   /// Boolean determining whether the background rectangle is displayable or not.
87   bool _displayableBackgroundRect;
88 
89   /// Boolean determining whether the background picture is displayable or not.
90   bool _displayableBackgroundPicture;
91 
92   public:
93   final:
94   /// Creates a new graphics wrapper.
95   this() {
96     _backgroundPaint = transparent;
97     _foregroundPaint = transparent;
98 
99     _size = new Size(0,0);
100     _position = new Point(0,0);
101 
102     _fontSize = 13;
103     _displayableBackgroundRect = true;
104     _displayableBackgroundPicture = true;
105     _originalBackgroundRect = OriginalSpace!(float,float)(0,0,0,0);
106     _originalBackgroundPicture = OriginalSpace!(ptrdiff_t, int)(0,0,0,0);
107   }
108 
109   @property {
110     /// Gets the background paint.
111     Paint backgroundPaint() { return _backgroundPaint; }
112 
113     /// Sets the background paint.
114     void backgroundPaint(Paint newPaint) {
115       _backgroundPaint = newPaint;
116 
117       if (_backgroundRect) {
118         _backgroundRect.fillColor = _backgroundPaint.sfmlColor;
119       }
120     }
121 
122     /// Gets the foreground paint.
123     Paint foregroundPaint() { return _foregroundPaint; }
124 
125     /// Sets the foreground paint.
126     void foregroundPaint(Paint newPaint) {
127       _foregroundPaint = newPaint;
128     }
129 
130     /// Gets the low-level background rectangle.
131     RectangleShape backgroundRect() { return _backgroundRect; }
132 
133     /// Gets the background picture.
134     Picture backgroundPicture() { return _backgroundPicture; }
135 
136     /// Sets the background picture.
137     void backgroundPicture(Picture newBackgroundPicture) {
138       _backgroundPicture = newBackgroundPicture;
139 
140       if (_backgroundPicture) {
141         _backgroundPicture.position = _position;
142 
143         _originalBackgroundPicture.x = _position.x;
144         _originalBackgroundPicture.y = _position.y;
145 
146         _originalBackgroundPicture.width = _size.width;
147         _originalBackgroundPicture.height = _size.height;
148       }
149     }
150 
151     /// Gets the font.
152     Font font() { return _font; }
153 
154     /// Sets the font.
155     void font(Font newFont) {
156       _font = newFont;
157     }
158 
159     /// Gets the font size.
160     uint fontSize() { return _fontSize; }
161 
162     /// Sets the font size.
163     void fontSize(uint newFontSize) {
164       _fontSize = newFontSize;
165     }
166   }
167 
168   package(poison):
169   @property {
170     /// Sets the position of the graphics.
171     void position(Point newPosition) {
172       _position = newPosition;
173 
174       if (_backgroundRect) {
175         _backgroundRect.position = Vector2f(cast(float)_position.x, cast(float)_position.y);
176         _originalBackgroundRect.x = _backgroundRect.position.x;
177         _originalBackgroundRect.y = _backgroundRect.position.y;
178       }
179 
180       if (_backgroundPicture) {
181         _backgroundPicture.position = _position;
182         _originalBackgroundPicture.x = cast(int)_position.x;
183         _originalBackgroundPicture.y = cast(int)_position.y;
184       }
185     }
186   }
187 
188   /// Sets the size of the graphics.
189   void size(Size newSize) {
190     _size = newSize;
191 
192     _backgroundRect = new RectangleShape(Vector2f(cast(float)_size.width, cast(float)_size.height));
193     backgroundPaint = _backgroundPaint;
194     position = _position;
195 
196     _originalBackgroundRect.width = _backgroundRect.size.x;
197     _originalBackgroundRect.height = _backgroundRect.size.y;
198 
199     _originalBackgroundPicture.width = cast(int)_size.width;
200     _originalBackgroundPicture.height = cast(int)_size.height;
201   }
202 
203   package(poison):
204   @property {
205     /// Gets a boolean determining whether the background rectangle is displayable.
206     bool displayableBackgroundRect() { return _displayableBackgroundRect; }
207 
208     /// Gets a boolean determining whether the background picture is displayable.
209     bool displayableBackgroundPicture() { return _displayableBackgroundPicture; }
210   }
211 
212   /**
213   * Renders the sub rectangles for the graphics elements.
214   * Params:
215   *   position =  The position to be relative to.
216   *   size =      The size to be relative to.
217   */
218   void renderSub(Point position, Size size) {
219     import dsfml.graphics : IntRect;
220     import dsfml.system : Vector2f;
221 
222     SubRect calculateOverflow(float targetX, float targetY, float targetWidth, float targetHeight) {
223       import std.math : fmax;
224 
225       float overflowTop = fmax(0, (cast(float)position.y - targetY));
226       float overflowRight = fmax(0, (targetX + targetWidth) - (cast(float)position.x + cast(float)size.width));
227       float overflowBottom = fmax(0, (targetY + targetHeight) - (cast(float)position.y + cast(float)size.height));
228       float overflowLeft = fmax(0, (cast(float)position.x - targetX));
229 
230       targetY += overflowTop;
231       targetHeight -= overflowTop;
232       targetWidth -= overflowRight;
233       targetHeight -= overflowBottom;
234       targetX += overflowLeft;
235       targetWidth -= overflowLeft;
236 
237       return SubRect(targetX, targetY, targetWidth, targetHeight, overflowTop, overflowRight, overflowBottom, overflowLeft);
238     }
239 
240     bool intersect(float width, float height, float x, float y) {
241       return(x < cast(float)position.x + cast(float)size.width) &&
242         (cast(float)position.x < (x + width)) &&
243         (y < cast(float)position.y + cast(float)size.height) &&
244         (cast(float)position.y < y + height);
245      }
246 
247     if (_backgroundRect) {
248       _backgroundRect.size = Vector2f(_originalBackgroundRect.width, _originalBackgroundRect.height);
249       _backgroundRect.position = Vector2f(_originalBackgroundRect.x, _originalBackgroundRect.y);
250 
251       auto rectSize = _backgroundRect.size;
252       auto rectPosition = _backgroundRect.position;
253 
254       _displayableBackgroundRect = intersect(rectSize.x, rectSize.y, rectPosition.x, rectPosition.y);
255 
256       if (_displayableBackgroundRect) {
257         auto overflow = calculateOverflow(rectPosition.x, rectPosition.y, rectSize.x, rectSize.y);
258 
259         _backgroundRect.size = Vector2f(overflow.width, overflow.height);
260         _backgroundRect.position = Vector2f(overflow.x, overflow.y);
261       }
262     }
263 
264     if (_backgroundPicture) {
265       _displayableBackgroundPicture = intersect(cast(float)_originalBackgroundPicture.x, cast(float)_originalBackgroundPicture.y, cast(float)_originalBackgroundPicture.width, cast(float)_originalBackgroundPicture.height);
266 
267       if (_displayableBackgroundPicture) {
268         auto backgroundSprite = _backgroundPicture.backgroundSprite;
269         auto drawingSprite = _backgroundPicture.drawingSprite;
270 
271         if (backgroundSprite) {
272           backgroundSprite.position = new Point(_originalBackgroundPicture.x, _originalBackgroundPicture.y);
273           backgroundSprite.textureRect = IntRect(0, 0, cast(int)_originalBackgroundPicture.width, cast(int)_originalBackgroundPicture.height);
274         }
275 
276         if (drawingSprite) {
277           drawingSprite.position = new Point(_originalBackgroundPicture.x, _originalBackgroundPicture.y);
278           drawingSprite.textureRect = IntRect(0, 0, cast(int)_originalBackgroundPicture.width, cast(int)_originalBackgroundPicture.height);
279         }
280 
281         auto overflow = calculateOverflow(cast(float)_originalBackgroundPicture.x, cast(float)_originalBackgroundPicture.y, cast(float)_originalBackgroundPicture.width, cast(float)_originalBackgroundPicture.height);
282 
283         if (backgroundSprite) {
284           backgroundSprite.textureRect = IntRect(cast(int)overflow.overflowLeft, cast(int)overflow.overflowTop, cast(int)overflow.width, cast(int)overflow.height);
285           backgroundSprite.position = new Point(cast(ptrdiff_t)overflow.x, cast(ptrdiff_t)overflow.y);
286         }
287 
288         if (drawingSprite) {
289           drawingSprite.textureRect = IntRect(cast(int)overflow.overflowLeft, cast(int)overflow.overflowTop, cast(int)overflow.width, cast(int)overflow.height);
290           drawingSprite.position = new Point(cast(ptrdiff_t)overflow.x, cast(ptrdiff_t)overflow.y);
291         }
292       }
293     }
294   }
295 }