1 | /**************************************************************************/ |
2 | /* style_box_flat.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "style_box_flat.h" |
32 | |
33 | #include "servers/rendering_server.h" |
34 | |
35 | float StyleBoxFlat::get_style_margin(Side p_side) const { |
36 | ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); |
37 | return border_width[p_side]; |
38 | } |
39 | |
40 | void StyleBoxFlat::_validate_property(PropertyInfo &p_property) const { |
41 | if (!anti_aliased && p_property.name == "anti_aliasing_size" ) { |
42 | p_property.usage = PROPERTY_USAGE_NO_EDITOR; |
43 | } |
44 | } |
45 | |
46 | void StyleBoxFlat::set_bg_color(const Color &p_color) { |
47 | bg_color = p_color; |
48 | emit_changed(); |
49 | } |
50 | |
51 | Color StyleBoxFlat::get_bg_color() const { |
52 | return bg_color; |
53 | } |
54 | |
55 | void StyleBoxFlat::set_border_color(const Color &p_color) { |
56 | border_color = p_color; |
57 | emit_changed(); |
58 | } |
59 | |
60 | Color StyleBoxFlat::get_border_color() const { |
61 | return border_color; |
62 | } |
63 | |
64 | void StyleBoxFlat::set_border_width_all(int p_size) { |
65 | border_width[0] = p_size; |
66 | border_width[1] = p_size; |
67 | border_width[2] = p_size; |
68 | border_width[3] = p_size; |
69 | emit_changed(); |
70 | } |
71 | |
72 | int StyleBoxFlat::get_border_width_min() const { |
73 | return MIN(MIN(border_width[0], border_width[1]), MIN(border_width[2], border_width[3])); |
74 | } |
75 | |
76 | void StyleBoxFlat::set_border_width(Side p_side, int p_width) { |
77 | ERR_FAIL_INDEX((int)p_side, 4); |
78 | border_width[p_side] = p_width; |
79 | emit_changed(); |
80 | } |
81 | |
82 | int StyleBoxFlat::get_border_width(Side p_side) const { |
83 | ERR_FAIL_INDEX_V((int)p_side, 4, 0); |
84 | return border_width[p_side]; |
85 | } |
86 | |
87 | void StyleBoxFlat::set_border_blend(bool p_blend) { |
88 | blend_border = p_blend; |
89 | emit_changed(); |
90 | } |
91 | |
92 | bool StyleBoxFlat::get_border_blend() const { |
93 | return blend_border; |
94 | } |
95 | |
96 | void StyleBoxFlat::set_corner_radius(const Corner p_corner, const int radius) { |
97 | ERR_FAIL_INDEX((int)p_corner, 4); |
98 | corner_radius[p_corner] = radius; |
99 | emit_changed(); |
100 | } |
101 | |
102 | void StyleBoxFlat::set_corner_radius_all(int radius) { |
103 | for (int i = 0; i < 4; i++) { |
104 | corner_radius[i] = radius; |
105 | } |
106 | |
107 | emit_changed(); |
108 | } |
109 | |
110 | void StyleBoxFlat::set_corner_radius_individual(const int radius_top_left, const int radius_top_right, const int radius_bottom_right, const int radius_bottom_left) { |
111 | corner_radius[0] = radius_top_left; |
112 | corner_radius[1] = radius_top_right; |
113 | corner_radius[2] = radius_bottom_right; |
114 | corner_radius[3] = radius_bottom_left; |
115 | |
116 | emit_changed(); |
117 | } |
118 | |
119 | int StyleBoxFlat::get_corner_radius(const Corner p_corner) const { |
120 | ERR_FAIL_INDEX_V((int)p_corner, 4, 0); |
121 | return corner_radius[p_corner]; |
122 | } |
123 | |
124 | void StyleBoxFlat::set_corner_detail(const int &p_corner_detail) { |
125 | corner_detail = CLAMP(p_corner_detail, 1, 20); |
126 | emit_changed(); |
127 | } |
128 | |
129 | int StyleBoxFlat::get_corner_detail() const { |
130 | return corner_detail; |
131 | } |
132 | |
133 | void StyleBoxFlat::set_expand_margin(Side p_side, float p_size) { |
134 | ERR_FAIL_INDEX((int)p_side, 4); |
135 | expand_margin[p_side] = p_size; |
136 | emit_changed(); |
137 | } |
138 | |
139 | void StyleBoxFlat::set_expand_margin_all(float p_expand_margin_size) { |
140 | for (int i = 0; i < 4; i++) { |
141 | expand_margin[i] = p_expand_margin_size; |
142 | } |
143 | emit_changed(); |
144 | } |
145 | |
146 | void StyleBoxFlat::set_expand_margin_individual(float p_left, float p_top, float p_right, float p_bottom) { |
147 | expand_margin[SIDE_LEFT] = p_left; |
148 | expand_margin[SIDE_TOP] = p_top; |
149 | expand_margin[SIDE_RIGHT] = p_right; |
150 | expand_margin[SIDE_BOTTOM] = p_bottom; |
151 | emit_changed(); |
152 | } |
153 | |
154 | float StyleBoxFlat::get_expand_margin(Side p_side) const { |
155 | ERR_FAIL_INDEX_V((int)p_side, 4, 0.0); |
156 | return expand_margin[p_side]; |
157 | } |
158 | |
159 | void StyleBoxFlat::set_draw_center(bool p_enabled) { |
160 | draw_center = p_enabled; |
161 | emit_changed(); |
162 | } |
163 | |
164 | bool StyleBoxFlat::is_draw_center_enabled() const { |
165 | return draw_center; |
166 | } |
167 | |
168 | void StyleBoxFlat::set_skew(Vector2 p_skew) { |
169 | skew = p_skew; |
170 | emit_changed(); |
171 | } |
172 | |
173 | Vector2 StyleBoxFlat::get_skew() const { |
174 | return skew; |
175 | } |
176 | |
177 | void StyleBoxFlat::set_shadow_color(const Color &p_color) { |
178 | shadow_color = p_color; |
179 | emit_changed(); |
180 | } |
181 | |
182 | Color StyleBoxFlat::get_shadow_color() const { |
183 | return shadow_color; |
184 | } |
185 | |
186 | void StyleBoxFlat::set_shadow_size(const int &p_size) { |
187 | shadow_size = p_size; |
188 | emit_changed(); |
189 | } |
190 | |
191 | int StyleBoxFlat::get_shadow_size() const { |
192 | return shadow_size; |
193 | } |
194 | |
195 | void StyleBoxFlat::set_shadow_offset(const Point2 &p_offset) { |
196 | shadow_offset = p_offset; |
197 | emit_changed(); |
198 | } |
199 | |
200 | Point2 StyleBoxFlat::get_shadow_offset() const { |
201 | return shadow_offset; |
202 | } |
203 | |
204 | void StyleBoxFlat::set_anti_aliased(const bool &p_anti_aliased) { |
205 | anti_aliased = p_anti_aliased; |
206 | emit_changed(); |
207 | notify_property_list_changed(); |
208 | } |
209 | |
210 | bool StyleBoxFlat::is_anti_aliased() const { |
211 | return anti_aliased; |
212 | } |
213 | |
214 | void StyleBoxFlat::set_aa_size(const real_t p_aa_size) { |
215 | aa_size = CLAMP(p_aa_size, 0.01, 10); |
216 | emit_changed(); |
217 | } |
218 | |
219 | real_t StyleBoxFlat::get_aa_size() const { |
220 | return aa_size; |
221 | } |
222 | |
223 | inline void set_inner_corner_radius(const Rect2 style_rect, const Rect2 inner_rect, const real_t corner_radius[4], real_t *inner_corner_radius) { |
224 | real_t border_left = inner_rect.position.x - style_rect.position.x; |
225 | real_t border_top = inner_rect.position.y - style_rect.position.y; |
226 | real_t border_right = style_rect.size.width - inner_rect.size.width - border_left; |
227 | real_t border_bottom = style_rect.size.height - inner_rect.size.height - border_top; |
228 | |
229 | real_t rad; |
230 | |
231 | // Top left. |
232 | rad = MIN(border_top, border_left); |
233 | inner_corner_radius[0] = MAX(corner_radius[0] - rad, 0); |
234 | |
235 | // Top right; |
236 | rad = MIN(border_top, border_right); |
237 | inner_corner_radius[1] = MAX(corner_radius[1] - rad, 0); |
238 | |
239 | // Bottom right. |
240 | rad = MIN(border_bottom, border_right); |
241 | inner_corner_radius[2] = MAX(corner_radius[2] - rad, 0); |
242 | |
243 | // Bottom left. |
244 | rad = MIN(border_bottom, border_left); |
245 | inner_corner_radius[3] = MAX(corner_radius[3] - rad, 0); |
246 | } |
247 | |
248 | inline void draw_rounded_rectangle(Vector<Vector2> &verts, Vector<int> &indices, Vector<Color> &colors, const Rect2 &style_rect, const real_t corner_radius[4], |
249 | const Rect2 &ring_rect, const Rect2 &inner_rect, const Color &inner_color, const Color &outer_color, const int corner_detail, const Vector2 &skew, bool is_filled = false) { |
250 | int vert_offset = verts.size(); |
251 | if (!vert_offset) { |
252 | vert_offset = 0; |
253 | } |
254 | |
255 | int adapted_corner_detail = (corner_radius[0] == 0 && corner_radius[1] == 0 && corner_radius[2] == 0 && corner_radius[3] == 0) ? 1 : corner_detail; |
256 | |
257 | bool draw_border = !is_filled; |
258 | |
259 | real_t ring_corner_radius[4]; |
260 | set_inner_corner_radius(style_rect, ring_rect, corner_radius, ring_corner_radius); |
261 | |
262 | // Corner radius center points. |
263 | Vector<Point2> outer_points = { |
264 | ring_rect.position + Vector2(ring_corner_radius[0], ring_corner_radius[0]), //tl |
265 | Point2(ring_rect.position.x + ring_rect.size.x - ring_corner_radius[1], ring_rect.position.y + ring_corner_radius[1]), //tr |
266 | ring_rect.position + ring_rect.size - Vector2(ring_corner_radius[2], ring_corner_radius[2]), //br |
267 | Point2(ring_rect.position.x + ring_corner_radius[3], ring_rect.position.y + ring_rect.size.y - ring_corner_radius[3]) //bl |
268 | }; |
269 | |
270 | real_t inner_corner_radius[4]; |
271 | set_inner_corner_radius(style_rect, inner_rect, corner_radius, inner_corner_radius); |
272 | |
273 | Vector<Point2> inner_points = { |
274 | inner_rect.position + Vector2(inner_corner_radius[0], inner_corner_radius[0]), //tl |
275 | Point2(inner_rect.position.x + inner_rect.size.x - inner_corner_radius[1], inner_rect.position.y + inner_corner_radius[1]), //tr |
276 | inner_rect.position + inner_rect.size - Vector2(inner_corner_radius[2], inner_corner_radius[2]), //br |
277 | Point2(inner_rect.position.x + inner_corner_radius[3], inner_rect.position.y + inner_rect.size.y - inner_corner_radius[3]) //bl |
278 | }; |
279 | // Calculate the vertices. |
280 | |
281 | // If the center is filled, we do not draw the border and directly use the inner ring as reference. Because all calls to this |
282 | // method either draw a ring or a filled rounded rectangle, but not both. |
283 | int max_inner_outer = draw_border ? 2 : 1; |
284 | |
285 | for (int corner_index = 0; corner_index < 4; corner_index++) { |
286 | for (int detail = 0; detail <= adapted_corner_detail; detail++) { |
287 | for (int inner_outer = 0; inner_outer < max_inner_outer; inner_outer++) { |
288 | real_t radius; |
289 | Color color; |
290 | Point2 corner_point; |
291 | if (inner_outer == 0) { |
292 | radius = inner_corner_radius[corner_index]; |
293 | color = inner_color; |
294 | corner_point = inner_points[corner_index]; |
295 | } else { |
296 | radius = ring_corner_radius[corner_index]; |
297 | color = outer_color; |
298 | corner_point = outer_points[corner_index]; |
299 | } |
300 | |
301 | const real_t x = radius * (real_t)cos((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.x; |
302 | const real_t y = radius * (real_t)sin((corner_index + detail / (double)adapted_corner_detail) * (Math_TAU / 4.0) + Math_PI) + corner_point.y; |
303 | const float x_skew = -skew.x * (y - ring_rect.get_center().y); |
304 | const float y_skew = -skew.y * (x - ring_rect.get_center().x); |
305 | verts.push_back(Vector2(x + x_skew, y + y_skew)); |
306 | colors.push_back(color); |
307 | } |
308 | } |
309 | } |
310 | |
311 | int ring_vert_count = verts.size() - vert_offset; |
312 | |
313 | // Fill the indices and the colors for the border. |
314 | |
315 | if (draw_border) { |
316 | for (int i = 0; i < ring_vert_count; i++) { |
317 | indices.push_back(vert_offset + ((i + 0) % ring_vert_count)); |
318 | indices.push_back(vert_offset + ((i + 2) % ring_vert_count)); |
319 | indices.push_back(vert_offset + ((i + 1) % ring_vert_count)); |
320 | } |
321 | } |
322 | |
323 | if (is_filled) { |
324 | // Compute the triangles pattern to draw the rounded rectangle. |
325 | // Consists of vertical stripes of two triangles each. |
326 | |
327 | int stripes_count = ring_vert_count / 2 - 1; |
328 | int last_vert_id = ring_vert_count - 1; |
329 | |
330 | for (int i = 0; i < stripes_count; i++) { |
331 | // Polygon 1. |
332 | indices.push_back(vert_offset + i); |
333 | indices.push_back(vert_offset + last_vert_id - i - 1); |
334 | indices.push_back(vert_offset + i + 1); |
335 | // Polygon 2. |
336 | indices.push_back(vert_offset + i); |
337 | indices.push_back(vert_offset + last_vert_id - 0 - i); |
338 | indices.push_back(vert_offset + last_vert_id - 1 - i); |
339 | } |
340 | } |
341 | } |
342 | |
343 | inline void adapt_values(int p_index_a, int p_index_b, real_t *adapted_values, const real_t *p_values, const real_t p_width, const real_t p_max_a, const real_t p_max_b) { |
344 | if (p_values[p_index_a] + p_values[p_index_b] > p_width) { |
345 | real_t factor; |
346 | real_t new_value; |
347 | |
348 | factor = (real_t)p_width / (real_t)(p_values[p_index_a] + p_values[p_index_b]); |
349 | |
350 | new_value = (p_values[p_index_a] * factor); |
351 | if (new_value < adapted_values[p_index_a]) { |
352 | adapted_values[p_index_a] = new_value; |
353 | } |
354 | new_value = (p_values[p_index_b] * factor); |
355 | if (new_value < adapted_values[p_index_b]) { |
356 | adapted_values[p_index_b] = new_value; |
357 | } |
358 | } else { |
359 | adapted_values[p_index_a] = MIN(p_values[p_index_a], adapted_values[p_index_a]); |
360 | adapted_values[p_index_b] = MIN(p_values[p_index_b], adapted_values[p_index_b]); |
361 | } |
362 | adapted_values[p_index_a] = MIN(p_max_a, adapted_values[p_index_a]); |
363 | adapted_values[p_index_b] = MIN(p_max_b, adapted_values[p_index_b]); |
364 | } |
365 | |
366 | Rect2 StyleBoxFlat::get_draw_rect(const Rect2 &p_rect) const { |
367 | Rect2 draw_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); |
368 | |
369 | if (shadow_size > 0) { |
370 | Rect2 shadow_rect = draw_rect.grow(shadow_size); |
371 | shadow_rect.position += shadow_offset; |
372 | draw_rect = draw_rect.merge(shadow_rect); |
373 | } |
374 | |
375 | return draw_rect; |
376 | } |
377 | |
378 | void StyleBoxFlat::draw(RID p_canvas_item, const Rect2 &p_rect) const { |
379 | bool draw_border = (border_width[0] > 0) || (border_width[1] > 0) || (border_width[2] > 0) || (border_width[3] > 0); |
380 | bool draw_shadow = (shadow_size > 0); |
381 | if (!draw_border && !draw_center && !draw_shadow) { |
382 | return; |
383 | } |
384 | |
385 | Rect2 style_rect = p_rect.grow_individual(expand_margin[SIDE_LEFT], expand_margin[SIDE_TOP], expand_margin[SIDE_RIGHT], expand_margin[SIDE_BOTTOM]); |
386 | if (Math::is_zero_approx(style_rect.size.width) || Math::is_zero_approx(style_rect.size.height)) { |
387 | return; |
388 | } |
389 | |
390 | const bool rounded_corners = (corner_radius[0] > 0) || (corner_radius[1] > 0) || (corner_radius[2] > 0) || (corner_radius[3] > 0); |
391 | // Only enable antialiasing if it is actually needed. This improve performances |
392 | // and maximizes sharpness for non-skewed StyleBoxes with sharp corners. |
393 | const bool aa_on = (rounded_corners || !skew.is_zero_approx()) && anti_aliased; |
394 | |
395 | const bool blend_on = blend_border && draw_border; |
396 | |
397 | Color border_color_alpha = Color(border_color.r, border_color.g, border_color.b, 0); |
398 | Color border_color_blend = (draw_center ? bg_color : border_color_alpha); |
399 | Color border_color_inner = blend_on ? border_color_blend : border_color; |
400 | |
401 | // Adapt borders (prevent weird overlapping/glitchy drawings). |
402 | real_t width = MAX(style_rect.size.width, 0); |
403 | real_t height = MAX(style_rect.size.height, 0); |
404 | real_t adapted_border[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 }; |
405 | adapt_values(SIDE_TOP, SIDE_BOTTOM, adapted_border, border_width, height, height, height); |
406 | adapt_values(SIDE_LEFT, SIDE_RIGHT, adapted_border, border_width, width, width, width); |
407 | |
408 | // Adapt corners (prevent weird overlapping/glitchy drawings). |
409 | real_t adapted_corner[4] = { 1000000.0, 1000000.0, 1000000.0, 1000000.0 }; |
410 | adapt_values(CORNER_TOP_RIGHT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]); |
411 | adapt_values(CORNER_TOP_LEFT, CORNER_BOTTOM_LEFT, adapted_corner, corner_radius, height, height - adapted_border[SIDE_BOTTOM], height - adapted_border[SIDE_TOP]); |
412 | adapt_values(CORNER_TOP_LEFT, CORNER_TOP_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]); |
413 | adapt_values(CORNER_BOTTOM_LEFT, CORNER_BOTTOM_RIGHT, adapted_corner, corner_radius, width, width - adapted_border[SIDE_RIGHT], width - adapted_border[SIDE_LEFT]); |
414 | |
415 | Rect2 infill_rect = style_rect.grow_individual(-adapted_border[SIDE_LEFT], -adapted_border[SIDE_TOP], -adapted_border[SIDE_RIGHT], -adapted_border[SIDE_BOTTOM]); |
416 | |
417 | Rect2 border_style_rect = style_rect; |
418 | if (aa_on) { |
419 | for (int i = 0; i < 4; i++) { |
420 | if (border_width[i] > 0) { |
421 | border_style_rect = border_style_rect.grow_side((Side)i, -aa_size); |
422 | } |
423 | } |
424 | } |
425 | |
426 | Vector<Point2> verts; |
427 | Vector<int> indices; |
428 | Vector<Color> colors; |
429 | Vector<Point2> uvs; |
430 | |
431 | // Create shadow |
432 | if (draw_shadow) { |
433 | Rect2 shadow_inner_rect = style_rect; |
434 | shadow_inner_rect.position += shadow_offset; |
435 | |
436 | Rect2 shadow_rect = style_rect.grow(shadow_size); |
437 | shadow_rect.position += shadow_offset; |
438 | |
439 | Color shadow_color_transparent = Color(shadow_color.r, shadow_color.g, shadow_color.b, 0); |
440 | |
441 | draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner, |
442 | shadow_rect, shadow_inner_rect, shadow_color, shadow_color_transparent, corner_detail, skew); |
443 | |
444 | if (draw_center) { |
445 | draw_rounded_rectangle(verts, indices, colors, shadow_inner_rect, adapted_corner, |
446 | shadow_inner_rect, shadow_inner_rect, shadow_color, shadow_color, corner_detail, skew, true); |
447 | } |
448 | } |
449 | |
450 | // Create border (no AA). |
451 | if (draw_border && !aa_on) { |
452 | draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, |
453 | border_style_rect, infill_rect, border_color_inner, border_color, corner_detail, skew); |
454 | } |
455 | |
456 | // Create infill (no AA). |
457 | if (draw_center && (!aa_on || blend_on)) { |
458 | draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, |
459 | infill_rect, infill_rect, bg_color, bg_color, corner_detail, skew, true); |
460 | } |
461 | |
462 | if (aa_on) { |
463 | real_t aa_border_width[4]; |
464 | real_t aa_border_width_half[4]; |
465 | real_t aa_fill_width[4]; |
466 | real_t aa_fill_width_half[4]; |
467 | if (draw_border) { |
468 | for (int i = 0; i < 4; i++) { |
469 | if (border_width[i] > 0) { |
470 | aa_border_width[i] = aa_size; |
471 | aa_border_width_half[i] = aa_size / 2; |
472 | aa_fill_width[i] = 0; |
473 | aa_fill_width_half[i] = 0; |
474 | } else { |
475 | aa_border_width[i] = 0; |
476 | aa_border_width_half[i] = 0; |
477 | aa_fill_width[i] = aa_size; |
478 | aa_fill_width_half[i] = aa_size / 2; |
479 | } |
480 | } |
481 | } else { |
482 | for (int i = 0; i < 4; i++) { |
483 | aa_border_width[i] = 0; |
484 | aa_border_width_half[i] = 0; |
485 | aa_fill_width[i] = aa_size; |
486 | aa_fill_width_half[i] = aa_size / 2; |
487 | } |
488 | } |
489 | |
490 | if (draw_center) { |
491 | // Infill rect, transparent side of antialiasing gradient (base infill rect enlarged by AA size) |
492 | Rect2 infill_rect_aa_transparent = infill_rect.grow_individual(aa_fill_width_half[SIDE_LEFT], aa_fill_width_half[SIDE_TOP], |
493 | aa_fill_width_half[SIDE_RIGHT], aa_fill_width_half[SIDE_BOTTOM]); |
494 | // Infill rect, colored side of antialiasing gradient (base infill rect shrunk by AA size) |
495 | Rect2 infill_rect_aa_colored = infill_rect_aa_transparent.grow_individual(-aa_fill_width[SIDE_LEFT], -aa_fill_width[SIDE_TOP], |
496 | -aa_fill_width[SIDE_RIGHT], -aa_fill_width[SIDE_BOTTOM]); |
497 | if (!blend_on) { |
498 | // Create center fill, not antialiased yet |
499 | draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, |
500 | infill_rect_aa_colored, infill_rect_aa_colored, bg_color, bg_color, corner_detail, skew, true); |
501 | } |
502 | if (!blend_on || !draw_border) { |
503 | Color alpha_bg = Color(bg_color.r, bg_color.g, bg_color.b, 0); |
504 | // Add antialiasing on the center fill |
505 | draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, |
506 | infill_rect_aa_transparent, infill_rect_aa_colored, bg_color, alpha_bg, corner_detail, skew); |
507 | } |
508 | } |
509 | |
510 | if (draw_border) { |
511 | // Inner border recct, fully colored side of antialiasing gradient (base inner rect enlarged by AA size) |
512 | Rect2 inner_rect_aa_colored = infill_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP], |
513 | aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]); |
514 | // Inner border rect, transparent side of antialiasing gradient (base inner rect shrunk by AA size) |
515 | Rect2 inner_rect_aa_transparent = inner_rect_aa_colored.grow_individual(-aa_border_width[SIDE_LEFT], -aa_border_width[SIDE_TOP], |
516 | -aa_border_width[SIDE_RIGHT], -aa_border_width[SIDE_BOTTOM]); |
517 | // Outer border rect, transparent side of antialiasing gradient (base outer rect enlarged by AA size) |
518 | Rect2 outer_rect_aa_transparent = style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP], |
519 | aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]); |
520 | // Outer border rect, colored side of antialiasing gradient (base outer rect shrunk by AA size) |
521 | Rect2 outer_rect_aa_colored = border_style_rect.grow_individual(aa_border_width_half[SIDE_LEFT], aa_border_width_half[SIDE_TOP], |
522 | aa_border_width_half[SIDE_RIGHT], aa_border_width_half[SIDE_BOTTOM]); |
523 | |
524 | // Create border ring, not antialiased yet |
525 | draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, |
526 | outer_rect_aa_colored, ((blend_on) ? infill_rect : inner_rect_aa_colored), border_color_inner, border_color, corner_detail, skew); |
527 | if (!blend_on) { |
528 | // Add antialiasing on the ring inner border |
529 | draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, |
530 | inner_rect_aa_colored, inner_rect_aa_transparent, border_color_blend, border_color, corner_detail, skew); |
531 | } |
532 | // Add antialiasing on the ring outer border |
533 | draw_rounded_rectangle(verts, indices, colors, border_style_rect, adapted_corner, |
534 | outer_rect_aa_transparent, outer_rect_aa_colored, border_color, border_color_alpha, corner_detail, skew); |
535 | } |
536 | } |
537 | |
538 | // Compute UV coordinates. |
539 | Rect2 uv_rect = style_rect.grow(aa_on ? aa_size : 0); |
540 | uvs.resize(verts.size()); |
541 | for (int i = 0; i < verts.size(); i++) { |
542 | uvs.write[i].x = (verts[i].x - uv_rect.position.x) / uv_rect.size.width; |
543 | uvs.write[i].y = (verts[i].y - uv_rect.position.y) / uv_rect.size.height; |
544 | } |
545 | |
546 | // Draw stylebox. |
547 | RenderingServer *vs = RenderingServer::get_singleton(); |
548 | vs->canvas_item_add_triangle_array(p_canvas_item, indices, verts, colors, uvs); |
549 | } |
550 | |
551 | void StyleBoxFlat::_bind_methods() { |
552 | ClassDB::bind_method(D_METHOD("set_bg_color" , "color" ), &StyleBoxFlat::set_bg_color); |
553 | ClassDB::bind_method(D_METHOD("get_bg_color" ), &StyleBoxFlat::get_bg_color); |
554 | |
555 | ClassDB::bind_method(D_METHOD("set_border_color" , "color" ), &StyleBoxFlat::set_border_color); |
556 | ClassDB::bind_method(D_METHOD("get_border_color" ), &StyleBoxFlat::get_border_color); |
557 | |
558 | ClassDB::bind_method(D_METHOD("set_border_width_all" , "width" ), &StyleBoxFlat::set_border_width_all); |
559 | ClassDB::bind_method(D_METHOD("get_border_width_min" ), &StyleBoxFlat::get_border_width_min); |
560 | |
561 | ClassDB::bind_method(D_METHOD("set_border_width" , "margin" , "width" ), &StyleBoxFlat::set_border_width); |
562 | ClassDB::bind_method(D_METHOD("get_border_width" , "margin" ), &StyleBoxFlat::get_border_width); |
563 | |
564 | ClassDB::bind_method(D_METHOD("set_border_blend" , "blend" ), &StyleBoxFlat::set_border_blend); |
565 | ClassDB::bind_method(D_METHOD("get_border_blend" ), &StyleBoxFlat::get_border_blend); |
566 | |
567 | ClassDB::bind_method(D_METHOD("set_corner_radius_all" , "radius" ), &StyleBoxFlat::set_corner_radius_all); |
568 | |
569 | ClassDB::bind_method(D_METHOD("set_corner_radius" , "corner" , "radius" ), &StyleBoxFlat::set_corner_radius); |
570 | ClassDB::bind_method(D_METHOD("get_corner_radius" , "corner" ), &StyleBoxFlat::get_corner_radius); |
571 | |
572 | ClassDB::bind_method(D_METHOD("set_expand_margin" , "margin" , "size" ), &StyleBoxFlat::set_expand_margin); |
573 | ClassDB::bind_method(D_METHOD("set_expand_margin_all" , "size" ), &StyleBoxFlat::set_expand_margin_all); |
574 | ClassDB::bind_method(D_METHOD("get_expand_margin" , "margin" ), &StyleBoxFlat::get_expand_margin); |
575 | |
576 | ClassDB::bind_method(D_METHOD("set_draw_center" , "draw_center" ), &StyleBoxFlat::set_draw_center); |
577 | ClassDB::bind_method(D_METHOD("is_draw_center_enabled" ), &StyleBoxFlat::is_draw_center_enabled); |
578 | |
579 | ClassDB::bind_method(D_METHOD("set_skew" , "skew" ), &StyleBoxFlat::set_skew); |
580 | ClassDB::bind_method(D_METHOD("get_skew" ), &StyleBoxFlat::get_skew); |
581 | |
582 | ClassDB::bind_method(D_METHOD("set_shadow_color" , "color" ), &StyleBoxFlat::set_shadow_color); |
583 | ClassDB::bind_method(D_METHOD("get_shadow_color" ), &StyleBoxFlat::get_shadow_color); |
584 | |
585 | ClassDB::bind_method(D_METHOD("set_shadow_size" , "size" ), &StyleBoxFlat::set_shadow_size); |
586 | ClassDB::bind_method(D_METHOD("get_shadow_size" ), &StyleBoxFlat::get_shadow_size); |
587 | |
588 | ClassDB::bind_method(D_METHOD("set_shadow_offset" , "offset" ), &StyleBoxFlat::set_shadow_offset); |
589 | ClassDB::bind_method(D_METHOD("get_shadow_offset" ), &StyleBoxFlat::get_shadow_offset); |
590 | |
591 | ClassDB::bind_method(D_METHOD("set_anti_aliased" , "anti_aliased" ), &StyleBoxFlat::set_anti_aliased); |
592 | ClassDB::bind_method(D_METHOD("is_anti_aliased" ), &StyleBoxFlat::is_anti_aliased); |
593 | |
594 | ClassDB::bind_method(D_METHOD("set_aa_size" , "size" ), &StyleBoxFlat::set_aa_size); |
595 | ClassDB::bind_method(D_METHOD("get_aa_size" ), &StyleBoxFlat::get_aa_size); |
596 | |
597 | ClassDB::bind_method(D_METHOD("set_corner_detail" , "detail" ), &StyleBoxFlat::set_corner_detail); |
598 | ClassDB::bind_method(D_METHOD("get_corner_detail" ), &StyleBoxFlat::get_corner_detail); |
599 | |
600 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "bg_color" ), "set_bg_color" , "get_bg_color" ); |
601 | |
602 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_center" ), "set_draw_center" , "is_draw_center_enabled" ); |
603 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "skew" ), "set_skew" , "get_skew" ); |
604 | |
605 | ADD_GROUP("Border Width" , "border_width_" ); |
606 | ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_left" , PROPERTY_HINT_RANGE, "0,1024,1,suffix:px" ), "set_border_width" , "get_border_width" , SIDE_LEFT); |
607 | ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_top" , PROPERTY_HINT_RANGE, "0,1024,1,suffix:px" ), "set_border_width" , "get_border_width" , SIDE_TOP); |
608 | ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_right" , PROPERTY_HINT_RANGE, "0,1024,1,suffix:px" ), "set_border_width" , "get_border_width" , SIDE_RIGHT); |
609 | ADD_PROPERTYI(PropertyInfo(Variant::INT, "border_width_bottom" , PROPERTY_HINT_RANGE, "0,1024,1,suffix:px" ), "set_border_width" , "get_border_width" , SIDE_BOTTOM); |
610 | |
611 | ADD_GROUP("Border" , "border_" ); |
612 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "border_color" ), "set_border_color" , "get_border_color" ); |
613 | |
614 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "border_blend" ), "set_border_blend" , "get_border_blend" ); |
615 | |
616 | ADD_GROUP("Corner Radius" , "corner_radius_" ); |
617 | ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_left" , PROPERTY_HINT_RANGE, "0,1024,1,suffix:px" ), "set_corner_radius" , "get_corner_radius" , CORNER_TOP_LEFT); |
618 | ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_top_right" , PROPERTY_HINT_RANGE, "0,1024,1,suffix:px" ), "set_corner_radius" , "get_corner_radius" , CORNER_TOP_RIGHT); |
619 | ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_right" , PROPERTY_HINT_RANGE, "0,1024,1,suffix:px" ), "set_corner_radius" , "get_corner_radius" , CORNER_BOTTOM_RIGHT); |
620 | ADD_PROPERTYI(PropertyInfo(Variant::INT, "corner_radius_bottom_left" , PROPERTY_HINT_RANGE, "0,1024,1,suffix:px" ), "set_corner_radius" , "get_corner_radius" , CORNER_BOTTOM_LEFT); |
621 | |
622 | ADD_PROPERTY(PropertyInfo(Variant::INT, "corner_detail" , PROPERTY_HINT_RANGE, "1,20,1" ), "set_corner_detail" , "get_corner_detail" ); |
623 | |
624 | ADD_GROUP("Expand Margins" , "expand_margin_" ); |
625 | ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_left" , PROPERTY_HINT_RANGE, "0,2048,1,suffix:px" ), "set_expand_margin" , "get_expand_margin" , SIDE_LEFT); |
626 | ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_top" , PROPERTY_HINT_RANGE, "0,2048,1,suffix:px" ), "set_expand_margin" , "get_expand_margin" , SIDE_TOP); |
627 | ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_right" , PROPERTY_HINT_RANGE, "0,2048,1,suffix:px" ), "set_expand_margin" , "get_expand_margin" , SIDE_RIGHT); |
628 | ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "expand_margin_bottom" , PROPERTY_HINT_RANGE, "0,2048,1,suffix:px" ), "set_expand_margin" , "get_expand_margin" , SIDE_BOTTOM); |
629 | |
630 | ADD_GROUP("Shadow" , "shadow_" ); |
631 | ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color" ), "set_shadow_color" , "get_shadow_color" ); |
632 | ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size" , PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px" ), "set_shadow_size" , "get_shadow_size" ); |
633 | ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset" , PROPERTY_HINT_NONE, "suffix:px" ), "set_shadow_offset" , "get_shadow_offset" ); |
634 | |
635 | ADD_GROUP("Anti Aliasing" , "anti_aliasing_" ); |
636 | ADD_PROPERTY(PropertyInfo(Variant::BOOL, "anti_aliasing" ), "set_anti_aliased" , "is_anti_aliased" ); |
637 | ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anti_aliasing_size" , PROPERTY_HINT_RANGE, "0.01,10,0.001,suffix:px" ), "set_aa_size" , "get_aa_size" ); |
638 | } |
639 | |
640 | StyleBoxFlat::StyleBoxFlat() {} |
641 | |
642 | StyleBoxFlat::~StyleBoxFlat() {} |
643 | |