| 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 | |