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 | |
36 | void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) { |
37 | _set_texture(&under, p_texture); |
38 | } |
39 | |
40 | Ref<Texture2D> TextureProgressBar::get_under_texture() const { |
41 | return under; |
42 | } |
43 | |
44 | void TextureProgressBar::set_over_texture(const Ref<Texture2D> &p_texture) { |
45 | _set_texture(&over, p_texture); |
46 | } |
47 | |
48 | Ref<Texture2D> TextureProgressBar::get_over_texture() const { |
49 | return over; |
50 | } |
51 | |
52 | void 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 | |
64 | int 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 | |
69 | void 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 | |
79 | bool TextureProgressBar::get_nine_patch_stretch() const { |
80 | return nine_patch_stretch; |
81 | } |
82 | |
83 | Size2 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 | |
97 | void TextureProgressBar::set_progress_texture(const Ref<Texture2D> &p_texture) { |
98 | _set_texture(&progress, p_texture); |
99 | } |
100 | |
101 | Ref<Texture2D> TextureProgressBar::get_progress_texture() const { |
102 | return progress; |
103 | } |
104 | |
105 | void 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 | |
114 | Point2 TextureProgressBar::get_progress_offset() const { |
115 | return progress_offset; |
116 | } |
117 | |
118 | void 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 | |
127 | Color TextureProgressBar::get_tint_under() const { |
128 | return tint_under; |
129 | } |
130 | |
131 | void 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 | |
140 | Color TextureProgressBar::get_tint_progress() const { |
141 | return tint_progress; |
142 | } |
143 | |
144 | void 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 | |
153 | Color TextureProgressBar::get_tint_over() const { |
154 | return tint_over; |
155 | } |
156 | |
157 | void 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 | |
174 | void TextureProgressBar::_texture_changed() { |
175 | update_minimum_size(); |
176 | queue_redraw(); |
177 | } |
178 | |
179 | Point2 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 | |
243 | Point2 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 | |
256 | void 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 | |
429 | void 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 | |
609 | void 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 | |
620 | int TextureProgressBar::get_fill_mode() { |
621 | return mode; |
622 | } |
623 | |
624 | void 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 | |
640 | float TextureProgressBar::get_radial_initial_angle() { |
641 | return rad_init_angle; |
642 | } |
643 | |
644 | void 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 | |
655 | float TextureProgressBar::get_fill_degrees() { |
656 | return rad_max_degrees; |
657 | } |
658 | |
659 | void 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 | |
668 | Point2 TextureProgressBar::get_radial_center_offset() { |
669 | return rad_center_off; |
670 | } |
671 | |
672 | void 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 | |
748 | TextureProgressBar::TextureProgressBar() { |
749 | set_mouse_filter(MOUSE_FILTER_PASS); |
750 | } |
751 | |