1 module glui.border;
2 
3 import raylib;
4 
5 import glui.style;
6 
7 
8 @safe:
9 
10 
11 /// Interface for borders
12 interface GluiBorder {
13 
14     /// Apply the border, drawing it in the given box.
15     abstract void apply(Rectangle borderBox, uint[4] size) const;
16 
17     /// Get the rectangle for the given side of the border.
18     final Rectangle sideRect(Rectangle source, uint[4] size, Style.Side side) const {
19 
20         final switch (side) {
21 
22             // Left side
23             case Style.Side.left:
24                 return Rectangle(
25                     source.x,
26                     source.y + size.sideTop,
27                     size.sideLeft,
28                     source.height - size.sideTop - size.sideBottom,
29                 );
30 
31             // Right side
32             case Style.Side.right:
33                 return Rectangle(
34                     source.x + source.width - size.sideRight,
35                     source.y + size.sideTop,
36                     size.sideRight,
37                     source.height - size.sideTop - size.sideBottom,
38                 );
39 
40             // Top side
41             case Style.Side.top:
42                 return Rectangle(
43                     source.x + size.sideLeft,
44                     source.y,
45                     source.width - size.sideLeft - size.sideRight,
46                     size.sideTop
47                 );
48 
49             // Bottom side
50             case Style.Side.bottom:
51                 return Rectangle(
52                     source.x + size.sideLeft,
53                     source.y + source.height - size.sideBottom,
54                     source.width - size.sideLeft - size.sideRight,
55                     size.sideBottom
56                 );
57 
58         }
59 
60     }
61 
62     /// Get square for corner next counter-clockwise to the given side.
63     /// Note: returned rectangles may have negative size; rect start position will always point to the corner itself.
64     final Rectangle cornerRect(Rectangle source, uint[4] size, Style.Side side) const {
65 
66         final switch (side) {
67 
68             case Style.Side.left:
69                 return Rectangle(
70                     source.x,
71                     source.y + source.height,
72                     size.sideLeft,
73                     -cast(float) size.sideBottom,
74                 );
75 
76             case Style.Side.right:
77                 return Rectangle(
78                     source.x + source.width,
79                     source.y,
80                     -cast(float) size.sideRight,
81                     size.sideTop,
82                 );
83 
84             case Style.Side.top:
85                 return Rectangle(
86                     source.x,
87                     source.y,
88                     size.sideLeft,
89                     size.sideTop,
90                 );
91 
92             case Style.Side.bottom:
93                 return Rectangle(
94                     source.x + source.width,
95                     source.y + source.height,
96                     -cast(float) size.sideRight,
97                     -cast(float) size.sideBottom,
98                 );
99 
100         }
101 
102     }
103 
104 }
105 
106 
107 
108 ColorBorder colorBorder(Color color) {
109 
110     return colorBorder([color]);
111 
112 }
113 
114 ColorBorder colorBorder(size_t n)(Color[n] color) {
115 
116     auto result = new ColorBorder;
117     result.color = normalizeSideArray!Color(color);
118     return result;
119 
120 }
121 
122 class ColorBorder : GluiBorder {
123 
124     Color[4] color;
125 
126     void apply(Rectangle borderBox, uint[4] size) const @trusted {
127 
128         // For each side
129         foreach (sideIndex; 0..4) {
130 
131             const side = cast(Style.Side) sideIndex;
132             const nextSide = cast(Style.Side) ((sideIndex + 1) % 4);
133 
134             // Draw all the fragments
135             DrawRectangleRec(sideRect(borderBox, size, side), color[side]);
136 
137             // Draw triangles in the corner
138             foreach (shift; 0..2) {
139 
140                 // Get the corner
141                 const cornerSide = shiftSide(side, shift);
142 
143                 // Get corner parameters
144                 const corner = cornerRect(borderBox, size, cornerSide);
145                 const cornerStart = Vector2(corner.x, corner.y);
146                 const cornerSize = Vector2(corner.w, corner.h);
147                 const cornerEnd = side < 2
148                     ? Vector2(0, corner.h)
149                     : Vector2(corner.w, 0);
150 
151                 // Draw the first triangle
152                 if (!shift)
153                 DrawTriangle(
154                     cornerStart,
155                     cornerStart + cornerSize,
156                     cornerStart + cornerEnd,
157                     color[side],
158                 );
159 
160                 // Draw the second one
161                 else
162                 DrawTriangle(
163                     cornerStart,
164                     cornerStart + cornerEnd,
165                     cornerStart + cornerSize,
166                     color[side],
167                 );
168 
169             }
170 
171         }
172 
173     }
174 
175 }