1/**************************************************************************/
2/* texture_progress_bar.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 "texture_progress_bar.h"
32
33#include "core/config/engine.h"
34#include "scene/resources/atlas_texture.h"
35
36void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) {
37 _set_texture(&under, p_texture);
38}
39
40Ref<Texture2D> TextureProgressBar::get_under_texture() const {
41 return under;
42}
43
44void TextureProgressBar::set_over_texture(const Ref<Texture2D> &p_texture) {
45 _set_texture(&over, p_texture);
46}
47
48Ref<Texture2D> TextureProgressBar::get_over_texture() const {
49 return over;
50}
51
52void TextureProgressBar::set_stretch_margin(Side p_side, int p_size) {
53 ERR_FAIL_INDEX((int)p_side, 4);
54
55 if (stretch_margin[p_side] == p_size) {
56 return;
57 }
58
59 stretch_margin[p_side] = p_size;
60 queue_redraw();
61 update_minimum_size();
62}
63
64int TextureProgressBar::get_stretch_margin(Side p_side) const {
65 ERR_FAIL_INDEX_V((int)p_side, 4, 0);
66 return stretch_margin[p_side];
67}
68
69void TextureProgressBar::set_nine_patch_stretch(bool p_stretch) {
70 if (nine_patch_stretch == p_stretch) {
71 return;
72 }
73
74 nine_patch_stretch = p_stretch;
75 queue_redraw();
76 update_minimum_size();
77}
78
79bool TextureProgressBar::get_nine_patch_stretch() const {
80 return nine_patch_stretch;
81}
82
83Size2 TextureProgressBar::get_minimum_size() const {
84 if (nine_patch_stretch) {
85 return Size2(stretch_margin[SIDE_LEFT] + stretch_margin[SIDE_RIGHT], stretch_margin[SIDE_TOP] + stretch_margin[SIDE_BOTTOM]);
86 } else if (under.is_valid()) {
87 return under->get_size();
88 } else if (over.is_valid()) {
89 return over->get_size();
90 } else if (progress.is_valid()) {
91 return progress->get_size();
92 }
93
94 return Size2(1, 1);
95}
96
97void TextureProgressBar::set_progress_texture(const Ref<Texture2D> &p_texture) {
98 _set_texture(&progress, p_texture);
99}
100
101Ref<Texture2D> TextureProgressBar::get_progress_texture() const {
102 return progress;
103}
104
105void TextureProgressBar::set_progress_offset(Point2 p_offset) {
106 if (progress_offset == p_offset) {
107 return;
108 }
109
110 progress_offset = p_offset;
111 queue_redraw();
112}
113
114Point2 TextureProgressBar::get_progress_offset() const {
115 return progress_offset;
116}
117
118void TextureProgressBar::set_tint_under(const Color &p_tint) {
119 if (tint_under == p_tint) {
120 return;
121 }
122
123 tint_under = p_tint;
124 queue_redraw();
125}
126
127Color TextureProgressBar::get_tint_under() const {
128 return tint_under;
129}
130
131void TextureProgressBar::set_tint_progress(const Color &p_tint) {
132 if (tint_progress == p_tint) {
133 return;
134 }
135
136 tint_progress = p_tint;
137 queue_redraw();
138}
139
140Color TextureProgressBar::get_tint_progress() const {
141 return tint_progress;
142}
143
144void TextureProgressBar::set_tint_over(const Color &p_tint) {
145 if (tint_over == p_tint) {
146 return;
147 }
148
149 tint_over = p_tint;
150 queue_redraw();
151}
152
153Color TextureProgressBar::get_tint_over() const {
154 return tint_over;
155}
156
157void TextureProgressBar::_set_texture(Ref<Texture2D> *p_destination, const Ref<Texture2D> &p_texture) {
158 DEV_ASSERT(p_destination);
159 Ref<Texture2D> &destination = *p_destination;
160 if (destination == p_texture) {
161 return;
162 }
163 if (destination.is_valid()) {
164 destination->disconnect_changed(callable_mp(this, &TextureProgressBar::_texture_changed));
165 }
166 destination = p_texture;
167 if (destination.is_valid()) {
168 // Pass `CONNECT_REFERENCE_COUNTED` to avoid early disconnect in case the same texture is assigned to different "slots".
169 destination->connect_changed(callable_mp(this, &TextureProgressBar::_texture_changed), CONNECT_REFERENCE_COUNTED);
170 }
171 _texture_changed();
172}
173
174void TextureProgressBar::_texture_changed() {
175 update_minimum_size();
176 queue_redraw();
177}
178
179Point2 TextureProgressBar::unit_val_to_uv(float val) {
180 if (progress.is_null()) {
181 return Point2();
182 }
183
184 if (val < 0) {
185 val += 1;
186 }
187 if (val > 1) {
188 val -= 1;
189 }
190
191 Point2 p = get_relative_center();
192
193 // Minimal version of Liang-Barsky clipping algorithm
194 float angle = (val * Math_TAU) - Math_PI * 0.5;
195 Point2 dir = Vector2(Math::cos(angle), Math::sin(angle));
196 float t1 = 1.0;
197 float cp = 0.0;
198 float cq = 0.0;
199 float cr = 0.0;
200 float edgeLeft = 0.0;
201 float edgeRight = 1.0;
202 float edgeBottom = 0.0;
203 float edgeTop = 1.0;
204
205 for (int edge = 0; edge < 4; edge++) {
206 if (edge == 0) {
207 if (dir.x > 0) {
208 continue;
209 }
210 cq = -(edgeLeft - p.x);
211 dir.x *= 2.0 * cq;
212 cp = -dir.x;
213 } else if (edge == 1) {
214 if (dir.x < 0) {
215 continue;
216 }
217 cq = (edgeRight - p.x);
218 dir.x *= 2.0 * cq;
219 cp = dir.x;
220 } else if (edge == 2) {
221 if (dir.y > 0) {
222 continue;
223 }
224 cq = -(edgeBottom - p.y);
225 dir.y *= 2.0 * cq;
226 cp = -dir.y;
227 } else if (edge == 3) {
228 if (dir.y < 0) {
229 continue;
230 }
231 cq = (edgeTop - p.y);
232 dir.y *= 2.0 * cq;
233 cp = dir.y;
234 }
235 cr = cq / cp;
236 if (cr >= 0 && cr < t1) {
237 t1 = cr;
238 }
239 }
240 return (p + t1 * dir);
241}
242
243Point2 TextureProgressBar::get_relative_center() {
244 if (progress.is_null()) {
245 return Point2();
246 }
247 Point2 p = progress->get_size() / 2;
248 p += rad_center_off;
249 p.x /= progress->get_width();
250 p.y /= progress->get_height();
251 p.x = CLAMP(p.x, 0, 1);
252 p.y = CLAMP(p.y, 0, 1);
253 return p;
254}
255
256void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) {
257 Vector2 texture_size = p_texture->get_size();
258 Vector2 topleft = Vector2(stretch_margin[SIDE_LEFT], stretch_margin[SIDE_TOP]);
259 Vector2 bottomright = Vector2(stretch_margin[SIDE_RIGHT], stretch_margin[SIDE_BOTTOM]);
260
261 Rect2 src_rect = Rect2(Point2(), texture_size);
262 Rect2 dst_rect = Rect2(Point2(), get_size());
263
264 if (p_ratio < 1.0) {
265 // Drawing a partially-filled 9-patch is a little tricky -
266 // texture is divided by 3 sections toward fill direction,
267 // then middle section is stretching while the other two aren't.
268
269 double width_total = 0.0;
270 double width_texture = 0.0;
271 double first_section_size = 0.0;
272 double last_section_size = 0.0;
273 switch (p_mode) {
274 case FILL_LEFT_TO_RIGHT: {
275 width_total = dst_rect.size.x;
276 width_texture = texture_size.x;
277 first_section_size = topleft.x;
278 last_section_size = bottomright.x;
279 } break;
280 case FILL_RIGHT_TO_LEFT: {
281 width_total = dst_rect.size.x;
282 width_texture = texture_size.x;
283 // In contrast to `FILL_LEFT_TO_RIGHT`, `first_section_size` and `last_section_size` should switch value.
284 first_section_size = bottomright.x;
285 last_section_size = topleft.x;
286 } break;
287 case FILL_TOP_TO_BOTTOM: {
288 width_total = dst_rect.size.y;
289 width_texture = texture_size.y;
290 first_section_size = topleft.y;
291 last_section_size = bottomright.y;
292 } break;
293 case FILL_BOTTOM_TO_TOP: {
294 width_total = dst_rect.size.y;
295 width_texture = texture_size.y;
296 // Similar to `FILL_RIGHT_TO_LEFT`.
297 first_section_size = bottomright.y;
298 last_section_size = topleft.y;
299 } break;
300 case FILL_BILINEAR_LEFT_AND_RIGHT: {
301 width_total = dst_rect.size.x;
302 width_texture = texture_size.x;
303 first_section_size = topleft.x;
304 last_section_size = bottomright.x;
305 } break;
306 case FILL_BILINEAR_TOP_AND_BOTTOM: {
307 width_total = dst_rect.size.y;
308 width_texture = texture_size.y;
309 first_section_size = topleft.y;
310 last_section_size = bottomright.y;
311 } break;
312 case FILL_CLOCKWISE:
313 case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE:
314 case FILL_COUNTER_CLOCKWISE: {
315 // Those modes are circular, not relevant for nine patch.
316 } break;
317 case FILL_MODE_MAX:
318 break;
319 }
320
321 double width_filled = width_total * p_ratio;
322 double middle_section_size = MAX(0.0, width_texture - first_section_size - last_section_size);
323
324 // Maximum middle texture size.
325 double max_middle_texture_size = middle_section_size;
326
327 // Maximum real middle texture size.
328 double max_middle_real_size = MAX(0.0, width_total - (first_section_size + last_section_size));
329
330 switch (p_mode) {
331 case FILL_BILINEAR_LEFT_AND_RIGHT:
332 case FILL_BILINEAR_TOP_AND_BOTTOM: {
333 last_section_size = MAX(0.0, last_section_size - (width_total - width_filled) * 0.5);
334 first_section_size = MAX(0.0, first_section_size - (width_total - width_filled) * 0.5);
335
336 // When `width_filled` increases, `middle_section_size` only increases when either of `first_section_size` and `last_section_size` is zero.
337 // Also, it should always be smaller than or equal to `(width_total - (first_section_size + last_section_size))`.
338 double real_middle_size = width_filled - first_section_size - last_section_size;
339 middle_section_size *= MIN(max_middle_real_size, real_middle_size) / max_middle_real_size;
340
341 width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size);
342 } break;
343 case FILL_MODE_MAX:
344 break;
345 default: {
346 middle_section_size *= MIN(1.0, (MAX(0.0, width_filled - first_section_size) / MAX(1.0, width_total - first_section_size - last_section_size)));
347 last_section_size = MAX(0.0, last_section_size - (width_total - width_filled));
348 first_section_size = MIN(first_section_size, width_filled);
349 width_texture = MIN(width_texture, first_section_size + middle_section_size + last_section_size);
350 }
351 }
352
353 switch (p_mode) {
354 case FILL_LEFT_TO_RIGHT: {
355 src_rect.size.x = width_texture;
356 dst_rect.size.x = width_filled;
357 topleft.x = first_section_size;
358 bottomright.x = last_section_size;
359 } break;
360 case FILL_RIGHT_TO_LEFT: {
361 src_rect.position.x += src_rect.size.x - width_texture;
362 src_rect.size.x = width_texture;
363 dst_rect.position.x += width_total - width_filled;
364 dst_rect.size.x = width_filled;
365 topleft.x = last_section_size;
366 bottomright.x = first_section_size;
367 } break;
368 case FILL_TOP_TO_BOTTOM: {
369 src_rect.size.y = width_texture;
370 dst_rect.size.y = width_filled;
371 bottomright.y = last_section_size;
372 topleft.y = first_section_size;
373 } break;
374 case FILL_BOTTOM_TO_TOP: {
375 src_rect.position.y += src_rect.size.y - width_texture;
376 src_rect.size.y = width_texture;
377 dst_rect.position.y += width_total - width_filled;
378 dst_rect.size.y = width_filled;
379 topleft.y = last_section_size;
380 bottomright.y = first_section_size;
381 } break;
382 case FILL_BILINEAR_LEFT_AND_RIGHT: {
383 double center_mapped_from_real_width = (width_total * 0.5 - topleft.x) / max_middle_real_size * max_middle_texture_size + topleft.x;
384 double drift_from_unscaled_center = 0;
385 if (bottomright.y != topleft.y) { // To avoid division by zero.
386 drift_from_unscaled_center = (src_rect.size.x * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.x - topleft.x);
387 }
388
389 src_rect.position.x += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5;
390 src_rect.size.x = width_texture;
391 dst_rect.position.x += (width_total - width_filled) * 0.5;
392 dst_rect.size.x = width_filled;
393 topleft.x = first_section_size;
394 bottomright.x = last_section_size;
395 } break;
396 case FILL_BILINEAR_TOP_AND_BOTTOM: {
397 double center_mapped_from_real_width = (width_total * 0.5 - topleft.y) / max_middle_real_size * max_middle_texture_size + topleft.y;
398 double drift_from_unscaled_center = 0;
399 if (bottomright.y != topleft.y) { // To avoid division by zero.
400 drift_from_unscaled_center = (src_rect.size.y * 0.5 - center_mapped_from_real_width) * (last_section_size - first_section_size) / (bottomright.y - topleft.y);
401 }
402
403 src_rect.position.y += center_mapped_from_real_width + drift_from_unscaled_center - width_texture * 0.5;
404 src_rect.size.y = width_texture;
405 dst_rect.position.y += (width_total - width_filled) * 0.5;
406 dst_rect.size.y = width_filled;
407 topleft.y = first_section_size;
408 bottomright.y = last_section_size;
409 } break;
410 case FILL_CLOCKWISE:
411 case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE:
412 case FILL_COUNTER_CLOCKWISE: {
413 // Those modes are circular, not relevant for nine patch.
414 } break;
415 case FILL_MODE_MAX:
416 break;
417 }
418 }
419
420 if (p_texture == progress) {
421 dst_rect.position += progress_offset;
422 }
423 p_texture->get_rect_region(dst_rect, src_rect, dst_rect, src_rect);
424
425 RID ci = get_canvas_item();
426 RS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright, RS::NINE_PATCH_STRETCH, RS::NINE_PATCH_STRETCH, true, p_modulate);
427}
428
429void TextureProgressBar::_notification(int p_what) {
430 switch (p_what) {
431 case NOTIFICATION_DRAW: {
432 if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP || mode == FILL_BILINEAR_LEFT_AND_RIGHT || mode == FILL_BILINEAR_TOP_AND_BOTTOM)) {
433 if (under.is_valid()) {
434 draw_nine_patch_stretched(under, mode, 1.0, tint_under);
435 }
436 if (progress.is_valid()) {
437 draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress);
438 }
439 if (over.is_valid()) {
440 draw_nine_patch_stretched(over, mode, 1.0, tint_over);
441 }
442 } else {
443 if (under.is_valid()) {
444 switch (mode) {
445 case FILL_CLOCKWISE:
446 case FILL_COUNTER_CLOCKWISE:
447 case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: {
448 if (nine_patch_stretch) {
449 Rect2 region = Rect2(Point2(), get_size());
450 draw_texture_rect(under, region, false, tint_under);
451 } else {
452 draw_texture(under, Point2(), tint_under);
453 }
454 } break;
455 case FILL_MODE_MAX:
456 break;
457 default:
458 draw_texture(under, Point2(), tint_under);
459 }
460 }
461 if (progress.is_valid()) {
462 Size2 s = progress->get_size();
463 switch (mode) {
464 case FILL_LEFT_TO_RIGHT: {
465 Rect2 region = Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y));
466 Rect2 source = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y));
467 draw_texture_rect_region(progress, region, source, tint_progress);
468 } break;
469 case FILL_RIGHT_TO_LEFT: {
470 Rect2 region = Rect2(progress_offset + Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y));
471 Rect2 source = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y));
472 draw_texture_rect_region(progress, region, source, tint_progress);
473 } break;
474 case FILL_TOP_TO_BOTTOM: {
475 Rect2 region = Rect2(progress_offset + Point2(), Size2(s.x, s.y * get_as_ratio()));
476 Rect2 source = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio()));
477 draw_texture_rect_region(progress, region, source, tint_progress);
478 } break;
479 case FILL_BOTTOM_TO_TOP: {
480 Rect2 region = Rect2(progress_offset + Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio()));
481 Rect2 source = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio()));
482 draw_texture_rect_region(progress, region, source, tint_progress);
483 } break;
484 case FILL_CLOCKWISE:
485 case FILL_COUNTER_CLOCKWISE:
486 case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: {
487 if (nine_patch_stretch) {
488 s = get_size();
489 }
490
491 float val = get_as_ratio() * rad_max_degrees / 360;
492 if (val == 1) {
493 Rect2 region = Rect2(progress_offset, s);
494 Rect2 source = Rect2(Point2(), progress->get_size());
495 draw_texture_rect_region(progress, region, source, tint_progress);
496 } else if (val != 0) {
497 Array pts;
498 float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1;
499 float start;
500
501 if (mode == FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE) {
502 start = rad_init_angle / 360 - val / 2;
503 } else {
504 start = rad_init_angle / 360;
505 }
506
507 float end = start + direction * val;
508 float from = MIN(start, end);
509 float to = MAX(start, end);
510 pts.append(from);
511 for (float corner = Math::floor(from * 4 + 0.5) * 0.25 + 0.125; corner < to; corner += 0.25) {
512 pts.append(corner);
513 }
514 pts.append(to);
515
516 Ref<AtlasTexture> atlas_progress = progress;
517 bool valid_atlas_progress = atlas_progress.is_valid() && atlas_progress->get_atlas().is_valid();
518 Rect2 region_rect;
519 Size2 atlas_size;
520 if (valid_atlas_progress) {
521 region_rect = atlas_progress->get_region();
522 atlas_size = atlas_progress->get_atlas()->get_size();
523 }
524
525 Vector<Point2> uvs;
526 Vector<Point2> points;
527 for (int i = 0; i < pts.size(); i++) {
528 Point2 uv = unit_val_to_uv(pts[i]);
529 if (uvs.find(uv) >= 0) {
530 continue;
531 }
532 points.push_back(progress_offset + Point2(uv.x * s.x, uv.y * s.y));
533 if (valid_atlas_progress) {
534 uv.x = Math::remap(uv.x, 0, 1, region_rect.position.x / atlas_size.x, (region_rect.position.x + region_rect.size.x) / atlas_size.x);
535 uv.y = Math::remap(uv.y, 0, 1, region_rect.position.y / atlas_size.y, (region_rect.position.y + region_rect.size.y) / atlas_size.y);
536 }
537 uvs.push_back(uv);
538 }
539
540 Point2 center_point = get_relative_center();
541 points.push_back(progress_offset + s * center_point);
542 if (valid_atlas_progress) {
543 center_point.x = Math::remap(center_point.x, 0, 1, region_rect.position.x / atlas_size.x, (region_rect.position.x + region_rect.size.x) / atlas_size.x);
544 center_point.y = Math::remap(center_point.y, 0, 1, region_rect.position.y / atlas_size.y, (region_rect.position.y + region_rect.size.y) / atlas_size.y);
545 }
546 uvs.push_back(center_point);
547
548 Vector<Color> colors;
549 colors.push_back(tint_progress);
550 draw_polygon(points, colors, uvs, progress);
551 }
552
553 // Draw a reference cross.
554 if (Engine::get_singleton()->is_editor_hint()) {
555 Point2 p;
556
557 if (nine_patch_stretch) {
558 p = get_size();
559 } else {
560 p = progress->get_size();
561 }
562
563 p *= get_relative_center();
564 p += progress_offset;
565 p = p.floor();
566 draw_line(p - Point2(8, 0), p + Point2(8, 0), Color(0.9, 0.5, 0.5), 2);
567 draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2);
568 }
569 } break;
570 case FILL_BILINEAR_LEFT_AND_RIGHT: {
571 Rect2 region = Rect2(progress_offset + Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y));
572 Rect2 source = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y));
573 draw_texture_rect_region(progress, region, source, tint_progress);
574 } break;
575 case FILL_BILINEAR_TOP_AND_BOTTOM: {
576 Rect2 region = Rect2(progress_offset + Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio()));
577 Rect2 source = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio()));
578 draw_texture_rect_region(progress, region, source, tint_progress);
579 } break;
580 case FILL_MODE_MAX:
581 break;
582 default:
583 draw_texture_rect_region(progress, Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress);
584 }
585 }
586 if (over.is_valid()) {
587 switch (mode) {
588 case FILL_CLOCKWISE:
589 case FILL_COUNTER_CLOCKWISE:
590 case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: {
591 if (nine_patch_stretch) {
592 Rect2 region = Rect2(Point2(), get_size());
593 draw_texture_rect(over, region, false, tint_over);
594 } else {
595 draw_texture(over, Point2(), tint_over);
596 }
597 } break;
598 case FILL_MODE_MAX:
599 break;
600 default:
601 draw_texture(over, Point2(), tint_over);
602 }
603 }
604 }
605 } break;
606 }
607}
608
609void TextureProgressBar::set_fill_mode(int p_fill) {
610 ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX);
611
612 if (mode == (FillMode)p_fill) {
613 return;
614 }
615
616 mode = (FillMode)p_fill;
617 queue_redraw();
618}
619
620int TextureProgressBar::get_fill_mode() {
621 return mode;
622}
623
624void TextureProgressBar::set_radial_initial_angle(float p_angle) {
625 while (p_angle > 360) {
626 p_angle -= 360;
627 }
628 while (p_angle < 0) {
629 p_angle += 360;
630 }
631
632 if (rad_init_angle == p_angle) {
633 return;
634 }
635
636 rad_init_angle = p_angle;
637 queue_redraw();
638}
639
640float TextureProgressBar::get_radial_initial_angle() {
641 return rad_init_angle;
642}
643
644void TextureProgressBar::set_fill_degrees(float p_angle) {
645 float angle_clamped = CLAMP(p_angle, 0, 360);
646
647 if (rad_max_degrees == angle_clamped) {
648 return;
649 }
650
651 rad_max_degrees = angle_clamped;
652 queue_redraw();
653}
654
655float TextureProgressBar::get_fill_degrees() {
656 return rad_max_degrees;
657}
658
659void TextureProgressBar::set_radial_center_offset(const Point2 &p_off) {
660 if (rad_center_off == p_off) {
661 return;
662 }
663
664 rad_center_off = p_off;
665 queue_redraw();
666}
667
668Point2 TextureProgressBar::get_radial_center_offset() {
669 return rad_center_off;
670}
671
672void TextureProgressBar::_bind_methods() {
673 ClassDB::bind_method(D_METHOD("set_under_texture", "tex"), &TextureProgressBar::set_under_texture);
674 ClassDB::bind_method(D_METHOD("get_under_texture"), &TextureProgressBar::get_under_texture);
675
676 ClassDB::bind_method(D_METHOD("set_progress_texture", "tex"), &TextureProgressBar::set_progress_texture);
677 ClassDB::bind_method(D_METHOD("get_progress_texture"), &TextureProgressBar::get_progress_texture);
678
679 ClassDB::bind_method(D_METHOD("set_over_texture", "tex"), &TextureProgressBar::set_over_texture);
680 ClassDB::bind_method(D_METHOD("get_over_texture"), &TextureProgressBar::get_over_texture);
681
682 ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgressBar::set_fill_mode);
683 ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgressBar::get_fill_mode);
684
685 ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgressBar::set_tint_under);
686 ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgressBar::get_tint_under);
687
688 ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgressBar::set_tint_progress);
689 ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgressBar::get_tint_progress);
690
691 ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgressBar::set_tint_over);
692 ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgressBar::get_tint_over);
693
694 ClassDB::bind_method(D_METHOD("set_texture_progress_offset", "offset"), &TextureProgressBar::set_progress_offset);
695 ClassDB::bind_method(D_METHOD("get_texture_progress_offset"), &TextureProgressBar::get_progress_offset);
696
697 ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgressBar::set_radial_initial_angle);
698 ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgressBar::get_radial_initial_angle);
699
700 ClassDB::bind_method(D_METHOD("set_radial_center_offset", "mode"), &TextureProgressBar::set_radial_center_offset);
701 ClassDB::bind_method(D_METHOD("get_radial_center_offset"), &TextureProgressBar::get_radial_center_offset);
702
703 ClassDB::bind_method(D_METHOD("set_fill_degrees", "mode"), &TextureProgressBar::set_fill_degrees);
704 ClassDB::bind_method(D_METHOD("get_fill_degrees"), &TextureProgressBar::get_fill_degrees);
705
706 ClassDB::bind_method(D_METHOD("set_stretch_margin", "margin", "value"), &TextureProgressBar::set_stretch_margin);
707 ClassDB::bind_method(D_METHOD("get_stretch_margin", "margin"), &TextureProgressBar::get_stretch_margin);
708
709 ClassDB::bind_method(D_METHOD("set_nine_patch_stretch", "stretch"), &TextureProgressBar::set_nine_patch_stretch);
710 ClassDB::bind_method(D_METHOD("get_nine_patch_stretch"), &TextureProgressBar::get_nine_patch_stretch);
711
712 ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode");
713 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "nine_patch_stretch"), "set_nine_patch_stretch", "get_nine_patch_stretch");
714
715 ADD_GROUP("Stretch Margin", "stretch_margin_");
716 ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_left", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_stretch_margin", "get_stretch_margin", SIDE_LEFT);
717 ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_top", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_stretch_margin", "get_stretch_margin", SIDE_TOP);
718 ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_right", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_stretch_margin", "get_stretch_margin", SIDE_RIGHT);
719 ADD_PROPERTYI(PropertyInfo(Variant::INT, "stretch_margin_bottom", PROPERTY_HINT_RANGE, "0,16384,1,suffix:px"), "set_stretch_margin", "get_stretch_margin", SIDE_BOTTOM);
720
721 ADD_GROUP("Textures", "texture_");
722 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_under_texture", "get_under_texture");
723 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_over_texture", "get_over_texture");
724 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_progress_texture", "get_progress_texture");
725 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_progress_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_progress_offset", "get_texture_progress_offset");
726
727 ADD_GROUP("Tint", "tint_");
728 ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under"), "set_tint_under", "get_tint_under");
729 ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over"), "set_tint_over", "get_tint_over");
730 ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress"), "set_tint_progress", "get_tint_progress");
731
732 ADD_GROUP("Radial Fill", "radial_");
733 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider,degrees"), "set_radial_initial_angle", "get_radial_initial_angle");
734 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider,degrees"), "set_fill_degrees", "get_fill_degrees");
735 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "radial_center_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_radial_center_offset", "get_radial_center_offset");
736
737 BIND_ENUM_CONSTANT(FILL_LEFT_TO_RIGHT);
738 BIND_ENUM_CONSTANT(FILL_RIGHT_TO_LEFT);
739 BIND_ENUM_CONSTANT(FILL_TOP_TO_BOTTOM);
740 BIND_ENUM_CONSTANT(FILL_BOTTOM_TO_TOP);
741 BIND_ENUM_CONSTANT(FILL_CLOCKWISE);
742 BIND_ENUM_CONSTANT(FILL_COUNTER_CLOCKWISE);
743 BIND_ENUM_CONSTANT(FILL_BILINEAR_LEFT_AND_RIGHT);
744 BIND_ENUM_CONSTANT(FILL_BILINEAR_TOP_AND_BOTTOM);
745 BIND_ENUM_CONSTANT(FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE);
746}
747
748TextureProgressBar::TextureProgressBar() {
749 set_mouse_filter(MOUSE_FILTER_PASS);
750}
751