1/**************************************************************************/
2/* color_picker.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 "color_picker.h"
32
33#include "core/input/input.h"
34#include "core/io/image.h"
35#include "core/math/color.h"
36#include "core/os/keyboard.h"
37#include "core/os/os.h"
38#include "scene/gui/color_mode.h"
39#include "scene/resources/image_texture.h"
40#include "scene/resources/style_box_flat.h"
41#include "scene/resources/style_box_texture.h"
42#include "scene/theme/theme_db.h"
43#include "servers/display_server.h"
44#include "thirdparty/misc/ok_color.h"
45#include "thirdparty/misc/ok_color_shader.h"
46
47List<Color> ColorPicker::preset_cache;
48List<Color> ColorPicker::recent_preset_cache;
49
50void ColorPicker::_notification(int p_what) {
51 switch (p_what) {
52 case NOTIFICATION_ENTER_TREE: {
53 _update_color();
54 } break;
55
56 case NOTIFICATION_THEME_CHANGED: {
57 btn_pick->set_icon(theme_cache.screen_picker);
58 _update_drop_down_arrow(btn_preset->is_pressed(), btn_preset);
59 _update_drop_down_arrow(btn_recent_preset->is_pressed(), btn_recent_preset);
60 btn_add_preset->set_icon(theme_cache.add_preset);
61
62 btn_pick->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
63 btn_shape->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
64 btn_mode->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
65
66 uv_edit->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
67 w_edit->set_custom_minimum_size(Size2(theme_cache.h_width, 0));
68
69 wheel_edit->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
70 wheel_margin->add_theme_constant_override("margin_bottom", 8 * theme_cache.base_scale);
71
72 for (int i = 0; i < SLIDER_COUNT; i++) {
73 labels[i]->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
74 sliders[i]->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
75 }
76 alpha_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
77 alpha_label->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
78
79 for (int i = 0; i < MODE_BUTTON_COUNT; i++) {
80 mode_btns[i]->add_theme_style_override(SNAME("pressed"), theme_cache.mode_button_pressed);
81 mode_btns[i]->add_theme_style_override(SNAME("normal"), theme_cache.mode_button_normal);
82 mode_btns[i]->add_theme_style_override(SNAME("hover"), theme_cache.mode_button_hover);
83 }
84
85 shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_HSV_RECTANGLE), theme_cache.shape_rect);
86 shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_HSV_WHEEL), theme_cache.shape_rect_wheel);
87 shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_VHS_CIRCLE), theme_cache.shape_circle);
88 shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_OKHSL_CIRCLE), theme_cache.shape_circle);
89
90 internal_margin->add_theme_constant_override(SNAME("margin_bottom"), theme_cache.content_margin);
91 internal_margin->add_theme_constant_override(SNAME("margin_left"), theme_cache.content_margin);
92 internal_margin->add_theme_constant_override(SNAME("margin_right"), theme_cache.content_margin);
93 internal_margin->add_theme_constant_override(SNAME("margin_top"), theme_cache.content_margin);
94
95 _reset_sliders_theme();
96
97 if (Engine::get_singleton()->is_editor_hint()) {
98 // Adjust for the width of the "Script" icon.
99 text_type->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
100 }
101
102 _update_presets();
103 _update_recent_presets();
104 _update_controls();
105 } break;
106
107 case NOTIFICATION_WM_CLOSE_REQUEST: {
108 if (picker_window != nullptr && picker_window->is_visible()) {
109 picker_window->hide();
110 }
111 } break;
112
113 case NOTIFICATION_INTERNAL_PROCESS: {
114 if (!is_picking_color) {
115 return;
116 }
117 set_pick_color(DisplayServer::get_singleton()->screen_get_pixel(DisplayServer::get_singleton()->mouse_get_position()));
118 }
119 }
120}
121
122void ColorPicker::_update_theme_item_cache() {
123 VBoxContainer::_update_theme_item_cache();
124
125 theme_cache.base_scale = get_theme_default_base_scale();
126}
127
128Ref<Shader> ColorPicker::wheel_shader;
129Ref<Shader> ColorPicker::circle_shader;
130Ref<Shader> ColorPicker::circle_ok_color_shader;
131
132void ColorPicker::init_shaders() {
133 wheel_shader.instantiate();
134 wheel_shader->set_code(R"(
135// ColorPicker wheel shader.
136
137shader_type canvas_item;
138
139void fragment() {
140 float x = UV.x - 0.5;
141 float y = UV.y - 0.5;
142 float a = atan(y, x);
143 x += 0.001;
144 y += 0.001;
145 float b = float(sqrt(x * x + y * y) < 0.5) * float(sqrt(x * x + y * y) > 0.42);
146 x -= 0.002;
147 float b2 = float(sqrt(x * x + y * y) < 0.5) * float(sqrt(x * x + y * y) > 0.42);
148 y -= 0.002;
149 float b3 = float(sqrt(x * x + y * y) < 0.5) * float(sqrt(x * x + y * y) > 0.42);
150 x += 0.002;
151 float b4 = float(sqrt(x * x + y * y) < 0.5) * float(sqrt(x * x + y * y) > 0.42);
152
153 COLOR = vec4(clamp((abs(fract(((a - TAU) / TAU) + vec3(3.0, 2.0, 1.0) / 3.0) * 6.0 - 3.0) - 1.0), 0.0, 1.0), (b + b2 + b3 + b4) / 4.00);
154}
155)");
156
157 circle_shader.instantiate();
158 circle_shader->set_code(R"(
159// ColorPicker circle shader.
160
161shader_type canvas_item;
162
163uniform float v = 1.0;
164
165void fragment() {
166 float x = UV.x - 0.5;
167 float y = UV.y - 0.5;
168 float a = atan(y, x);
169 x += 0.001;
170 y += 0.001;
171 float b = float(sqrt(x * x + y * y) < 0.5);
172 x -= 0.002;
173 float b2 = float(sqrt(x * x + y * y) < 0.5);
174 y -= 0.002;
175 float b3 = float(sqrt(x * x + y * y) < 0.5);
176 x += 0.002;
177 float b4 = float(sqrt(x * x + y * y) < 0.5);
178
179 COLOR = vec4(mix(vec3(1.0), clamp(abs(fract(vec3((a - TAU) / TAU) + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - vec3(3.0)) - vec3(1.0), 0.0, 1.0), ((float(sqrt(x * x + y * y)) * 2.0)) / 1.0) * vec3(v), (b + b2 + b3 + b4) / 4.00);
180})");
181
182 circle_ok_color_shader.instantiate();
183 circle_ok_color_shader->set_code(OK_COLOR_SHADER + R"(
184// ColorPicker ok color hsv circle shader.
185
186uniform float v = 1.0;
187
188void fragment() {
189 float x = UV.x - 0.5;
190 float y = UV.y - 0.5;
191 float h = atan(y, x) / (2.0 * M_PI);
192 float s = sqrt(x * x + y * y) * 2.0;
193 vec3 col = okhsl_to_srgb(vec3(h, s, v));
194 x += 0.001;
195 y += 0.001;
196 float b = float(sqrt(x * x + y * y) < 0.5);
197 x -= 0.002;
198 float b2 = float(sqrt(x * x + y * y) < 0.5);
199 y -= 0.002;
200 float b3 = float(sqrt(x * x + y * y) < 0.5);
201 x += 0.002;
202 float b4 = float(sqrt(x * x + y * y) < 0.5);
203 COLOR = vec4(col, (b + b2 + b3 + b4) / 4.00);
204})");
205}
206
207void ColorPicker::finish_shaders() {
208 wheel_shader.unref();
209 circle_shader.unref();
210 circle_ok_color_shader.unref();
211}
212
213void ColorPicker::set_focus_on_line_edit() {
214 c_text->call_deferred(SNAME("grab_focus"));
215}
216
217void ColorPicker::_update_controls() {
218 int mode_sliders_count = modes[current_mode]->get_slider_count();
219
220 for (int i = current_slider_count; i < mode_sliders_count; i++) {
221 sliders[i]->show();
222 labels[i]->show();
223 values[i]->show();
224 }
225 for (int i = mode_sliders_count; i < current_slider_count; i++) {
226 sliders[i]->hide();
227 labels[i]->hide();
228 values[i]->hide();
229 }
230 current_slider_count = mode_sliders_count;
231
232 for (int i = 0; i < current_slider_count; i++) {
233 labels[i]->set_text(modes[current_mode]->get_slider_label(i));
234 }
235 alpha_label->set_text("A");
236
237 slider_theme_modified = modes[current_mode]->apply_theme();
238
239 if (edit_alpha) {
240 alpha_value->show();
241 alpha_slider->show();
242 alpha_label->show();
243 } else {
244 alpha_value->hide();
245 alpha_slider->hide();
246 alpha_label->hide();
247 }
248
249 switch (_get_actual_shape()) {
250 case SHAPE_HSV_RECTANGLE:
251 wheel_edit->hide();
252 w_edit->show();
253 uv_edit->show();
254 btn_shape->show();
255 break;
256 case SHAPE_HSV_WHEEL:
257 wheel_edit->show();
258 w_edit->hide();
259 uv_edit->hide();
260 btn_shape->show();
261 wheel->set_material(wheel_mat);
262 break;
263 case SHAPE_VHS_CIRCLE:
264 wheel_edit->show();
265 w_edit->show();
266 uv_edit->hide();
267 btn_shape->show();
268 wheel->set_material(circle_mat);
269 circle_mat->set_shader(circle_shader);
270 break;
271 case SHAPE_OKHSL_CIRCLE:
272 wheel_edit->show();
273 w_edit->show();
274 uv_edit->hide();
275 btn_shape->show();
276 wheel->set_material(circle_mat);
277 circle_mat->set_shader(circle_ok_color_shader);
278 break;
279 case SHAPE_NONE:
280 wheel_edit->hide();
281 w_edit->hide();
282 uv_edit->hide();
283 btn_shape->hide();
284 break;
285 default: {
286 }
287 }
288}
289
290void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) {
291 if (text_changed) {
292 add_recent_preset(color);
293 text_changed = false;
294 }
295
296 color = p_color;
297 if (color != last_color) {
298 _copy_color_to_hsv();
299 last_color = color;
300 }
301
302 if (!is_inside_tree()) {
303 return;
304 }
305
306 _update_color(p_update_sliders);
307}
308
309void ColorPicker::set_pick_color(const Color &p_color) {
310 _set_pick_color(p_color, true); //because setters can't have more arguments
311}
312
313void ColorPicker::set_old_color(const Color &p_color) {
314 old_color = p_color;
315}
316
317void ColorPicker::set_display_old_color(bool p_enabled) {
318 display_old_color = p_enabled;
319}
320
321bool ColorPicker::is_displaying_old_color() const {
322 return display_old_color;
323}
324
325void ColorPicker::set_edit_alpha(bool p_show) {
326 if (edit_alpha == p_show) {
327 return;
328 }
329 edit_alpha = p_show;
330 _update_controls();
331
332 if (!is_inside_tree()) {
333 return;
334 }
335
336 _update_color();
337 sample->queue_redraw();
338}
339
340bool ColorPicker::is_editing_alpha() const {
341 return edit_alpha;
342}
343
344void ColorPicker::_value_changed(double) {
345 if (updating) {
346 return;
347 }
348
349 color = modes[current_mode]->get_color();
350 modes[current_mode]->_value_changed();
351
352 if (current_mode == MODE_HSV || current_mode == MODE_OKHSL) {
353 h = sliders[0]->get_value() / 360.0;
354 s = sliders[1]->get_value() / 100.0;
355 v = sliders[2]->get_value() / 100.0;
356 last_color = color;
357 }
358
359 _set_pick_color(color, false);
360 emit_signal(SNAME("color_changed"), color);
361}
362
363void ColorPicker::add_mode(ColorMode *p_mode) {
364 modes.push_back(p_mode);
365}
366
367void ColorPicker::create_slider(GridContainer *gc, int idx) {
368 Label *lbl = memnew(Label());
369 lbl->set_v_size_flags(SIZE_SHRINK_CENTER);
370 gc->add_child(lbl);
371
372 HSlider *slider = memnew(HSlider);
373 slider->set_v_size_flags(SIZE_SHRINK_CENTER);
374 slider->set_focus_mode(FOCUS_NONE);
375 gc->add_child(slider);
376
377 SpinBox *val = memnew(SpinBox);
378 slider->share(val);
379 val->set_select_all_on_focus(true);
380 gc->add_child(val);
381
382 LineEdit *vle = val->get_line_edit();
383 vle->connect("text_changed", callable_mp(this, &ColorPicker::_text_changed));
384 vle->connect("gui_input", callable_mp(this, &ColorPicker::_line_edit_input));
385 vle->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
386
387 val->connect("gui_input", callable_mp(this, &ColorPicker::_slider_or_spin_input));
388
389 slider->set_h_size_flags(SIZE_EXPAND_FILL);
390
391 slider->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed));
392 slider->connect("draw", callable_mp(this, &ColorPicker::_slider_draw).bind(idx));
393 slider->connect("gui_input", callable_mp(this, &ColorPicker::_slider_or_spin_input));
394
395 if (idx < SLIDER_COUNT) {
396 sliders[idx] = slider;
397 values[idx] = val;
398 labels[idx] = lbl;
399 } else {
400 alpha_slider = slider;
401 alpha_value = val;
402 alpha_label = lbl;
403 }
404}
405
406#ifdef TOOLS_ENABLED
407void ColorPicker::set_editor_settings(Object *p_editor_settings) {
408 if (editor_settings) {
409 return;
410 }
411 editor_settings = p_editor_settings;
412
413 if (preset_cache.is_empty()) {
414 PackedColorArray saved_presets = editor_settings->call(SNAME("get_project_metadata"), "color_picker", "presets", PackedColorArray());
415 for (int i = 0; i < saved_presets.size(); i++) {
416 preset_cache.push_back(saved_presets[i]);
417 }
418 }
419
420 for (int i = 0; i < preset_cache.size(); i++) {
421 presets.push_back(preset_cache[i]);
422 }
423
424 if (recent_preset_cache.is_empty()) {
425 PackedColorArray saved_recent_presets = editor_settings->call(SNAME("get_project_metadata"), "color_picker", "recent_presets", PackedColorArray());
426 for (int i = 0; i < saved_recent_presets.size(); i++) {
427 recent_preset_cache.push_back(saved_recent_presets[i]);
428 }
429 }
430
431 for (int i = 0; i < recent_preset_cache.size(); i++) {
432 recent_presets.push_back(recent_preset_cache[i]);
433 }
434
435 _update_presets();
436 _update_recent_presets();
437}
438#endif
439
440HSlider *ColorPicker::get_slider(int p_idx) {
441 if (p_idx < SLIDER_COUNT) {
442 return sliders[p_idx];
443 }
444 return alpha_slider;
445}
446
447Vector<float> ColorPicker::get_active_slider_values() {
448 Vector<float> cur_values;
449 for (int i = 0; i < current_slider_count; i++) {
450 cur_values.push_back(sliders[i]->get_value());
451 }
452 cur_values.push_back(alpha_slider->get_value());
453 return cur_values;
454}
455
456void ColorPicker::_copy_color_to_hsv() {
457 if (_get_actual_shape() == SHAPE_OKHSL_CIRCLE) {
458 h = color.get_ok_hsl_h();
459 s = color.get_ok_hsl_s();
460 v = color.get_ok_hsl_l();
461 } else {
462 h = color.get_h();
463 s = color.get_s();
464 v = color.get_v();
465 }
466}
467
468void ColorPicker::_copy_hsv_to_color() {
469 if (_get_actual_shape() == SHAPE_OKHSL_CIRCLE) {
470 color.set_ok_hsl(h, s, v, color.a);
471 } else {
472 color.set_hsv(h, s, v, color.a);
473 }
474}
475
476void ColorPicker::_select_from_preset_container(const Color &p_color) {
477 if (preset_group->get_pressed_button()) {
478 preset_group->get_pressed_button()->set_pressed(false);
479 }
480
481 for (int i = 1; i < preset_container->get_child_count(); i++) {
482 ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(preset_container->get_child(i));
483 if (current_btn && p_color == current_btn->get_preset_color()) {
484 current_btn->set_pressed(true);
485 break;
486 }
487 }
488}
489
490bool ColorPicker::_select_from_recent_preset_hbc(const Color &p_color) {
491 for (int i = 0; i < recent_preset_hbc->get_child_count(); i++) {
492 ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(recent_preset_hbc->get_child(i));
493 if (current_btn && p_color == current_btn->get_preset_color()) {
494 current_btn->set_pressed(true);
495 return true;
496 }
497 }
498 return false;
499}
500
501ColorPicker::PickerShapeType ColorPicker::_get_actual_shape() const {
502 return modes[current_mode]->get_shape_override() != SHAPE_MAX ? modes[current_mode]->get_shape_override() : current_shape;
503}
504
505void ColorPicker::_reset_sliders_theme() {
506 Ref<StyleBoxFlat> style_box_flat(memnew(StyleBoxFlat));
507 style_box_flat->set_content_margin(SIDE_TOP, 16 * theme_cache.base_scale);
508 style_box_flat->set_bg_color(Color(0.2, 0.23, 0.31).lerp(Color(0, 0, 0, 1), 0.3).clamp());
509 for (int i = 0; i < SLIDER_COUNT; i++) {
510 sliders[i]->add_theme_icon_override("grabber", theme_cache.bar_arrow);
511 sliders[i]->add_theme_icon_override("grabber_highlight", theme_cache.bar_arrow);
512 sliders[i]->add_theme_constant_override("grabber_offset", 8 * theme_cache.base_scale);
513 if (!colorize_sliders) {
514 sliders[i]->add_theme_style_override("slider", style_box_flat);
515 }
516 }
517 alpha_slider->add_theme_icon_override("grabber", theme_cache.bar_arrow);
518 alpha_slider->add_theme_icon_override("grabber_highlight", theme_cache.bar_arrow);
519 alpha_slider->add_theme_constant_override("grabber_offset", 8 * theme_cache.base_scale);
520 if (!colorize_sliders) {
521 alpha_slider->add_theme_style_override("slider", style_box_flat);
522 }
523}
524
525void ColorPicker::_html_submitted(const String &p_html) {
526 if (updating || text_is_constructor || !c_text->is_visible()) {
527 return;
528 }
529
530 const Color previous_color = color;
531 color = Color::from_string(p_html.strip_edges(), previous_color);
532
533 if (!is_editing_alpha()) {
534 color.a = previous_color.a;
535 }
536
537 if (color == previous_color) {
538 return;
539 }
540 if (!is_inside_tree()) {
541 return;
542 }
543
544 set_pick_color(color);
545 emit_signal(SNAME("color_changed"), color);
546}
547
548void ColorPicker::_update_color(bool p_update_sliders) {
549 updating = true;
550
551 if (p_update_sliders) {
552 float step = modes[current_mode]->get_slider_step();
553 for (int i = 0; i < current_slider_count; i++) {
554 sliders[i]->set_max(modes[current_mode]->get_slider_max(i));
555 sliders[i]->set_step(step);
556 sliders[i]->set_value(modes[current_mode]->get_slider_value(i));
557 }
558 alpha_slider->set_max(modes[current_mode]->get_slider_max(current_slider_count));
559 alpha_slider->set_step(step);
560 alpha_slider->set_value(modes[current_mode]->get_slider_value(current_slider_count));
561 }
562
563 _update_text_value();
564
565 sample->queue_redraw();
566 uv_edit->queue_redraw();
567 w_edit->queue_redraw();
568 for (int i = 0; i < current_slider_count; i++) {
569 sliders[i]->queue_redraw();
570 }
571 alpha_slider->queue_redraw();
572 wheel->queue_redraw();
573 wheel_uv->queue_redraw();
574 updating = false;
575}
576
577void ColorPicker::_update_presets() {
578 int preset_size = _get_preset_size();
579 // Only update the preset button size if it has changed.
580 if (preset_size != prev_preset_size) {
581 prev_preset_size = preset_size;
582 btn_add_preset->set_custom_minimum_size(Size2(preset_size, preset_size));
583 for (int i = 1; i < preset_container->get_child_count(); i++) {
584 ColorPresetButton *cpb = Object::cast_to<ColorPresetButton>(preset_container->get_child(i));
585 cpb->set_custom_minimum_size(Size2(preset_size, preset_size));
586 }
587 }
588
589#ifdef TOOLS_ENABLED
590 if (editor_settings) {
591 // Rebuild swatch color buttons, keeping the add-preset button in the first position.
592 for (int i = 1; i < preset_container->get_child_count(); i++) {
593 preset_container->get_child(i)->queue_free();
594 }
595 for (int i = 0; i < preset_cache.size(); i++) {
596 _add_preset_button(preset_size, preset_cache[i]);
597 }
598 _notification(NOTIFICATION_VISIBILITY_CHANGED);
599 }
600#endif
601}
602
603void ColorPicker::_update_recent_presets() {
604#ifdef TOOLS_ENABLED
605 if (editor_settings) {
606 int recent_preset_count = recent_preset_hbc->get_child_count();
607 for (int i = 0; i < recent_preset_count; i++) {
608 memdelete(recent_preset_hbc->get_child(0));
609 }
610
611 recent_presets.clear();
612 for (int i = 0; i < recent_preset_cache.size(); i++) {
613 recent_presets.push_back(recent_preset_cache[i]);
614 }
615
616 int preset_size = _get_preset_size();
617 for (int i = 0; i < recent_presets.size(); i++) {
618 _add_recent_preset_button(preset_size, recent_presets[i]);
619 }
620
621 _notification(NOTIFICATION_VISIBILITY_CHANGED);
622 }
623#endif
624}
625
626void ColorPicker::_text_type_toggled() {
627 text_is_constructor = !text_is_constructor;
628 if (text_is_constructor) {
629 text_type->set_text("");
630#ifdef TOOLS_ENABLED
631 text_type->set_icon(get_editor_theme_icon(SNAME("Script")));
632#endif
633
634 c_text->set_editable(false);
635 c_text->set_tooltip_text(RTR("Copy this constructor in a script."));
636 } else {
637 text_type->set_text("#");
638 text_type->set_icon(nullptr);
639
640 c_text->set_editable(true);
641 c_text->set_tooltip_text(RTR("Enter a hex code (\"#ff0000\") or named color (\"red\")."));
642 }
643 _update_color();
644}
645
646Color ColorPicker::get_pick_color() const {
647 return color;
648}
649
650void ColorPicker::set_picker_shape(PickerShapeType p_shape) {
651 ERR_FAIL_INDEX(p_shape, SHAPE_MAX);
652 if (p_shape == current_shape) {
653 return;
654 }
655 if (current_shape != SHAPE_NONE) {
656 shape_popup->set_item_checked(current_shape, false);
657 }
658 if (p_shape != SHAPE_NONE) {
659 shape_popup->set_item_checked(p_shape, true);
660 btn_shape->set_icon(shape_popup->get_item_icon(p_shape));
661 }
662
663 current_shape = p_shape;
664
665 _copy_color_to_hsv();
666
667 _update_controls();
668 _update_color();
669}
670
671ColorPicker::PickerShapeType ColorPicker::get_picker_shape() const {
672 return current_shape;
673}
674
675inline int ColorPicker::_get_preset_size() {
676 return (int(get_minimum_size().width) - (preset_container->get_h_separation() * (PRESET_COLUMN_COUNT - 1))) / PRESET_COLUMN_COUNT;
677}
678
679void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
680 ColorPresetButton *btn_preset_new = memnew(ColorPresetButton(p_color, p_size));
681 btn_preset_new->set_tooltip_text(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1)));
682 SET_DRAG_FORWARDING_GCDU(btn_preset_new, ColorPicker);
683 btn_preset_new->set_button_group(preset_group);
684 preset_container->add_child(btn_preset_new);
685 btn_preset_new->set_pressed(true);
686 btn_preset_new->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input).bind(p_color));
687}
688
689void ColorPicker::_add_recent_preset_button(int p_size, const Color &p_color) {
690 ColorPresetButton *btn_preset_new = memnew(ColorPresetButton(p_color, p_size));
691 btn_preset_new->set_tooltip_text(vformat(RTR("Color: #%s\nLMB: Apply color"), p_color.to_html(p_color.a < 1)));
692 btn_preset_new->set_button_group(recent_preset_group);
693 recent_preset_hbc->add_child(btn_preset_new);
694 recent_preset_hbc->move_child(btn_preset_new, 0);
695 btn_preset_new->set_pressed(true);
696 btn_preset_new->connect("toggled", callable_mp(this, &ColorPicker::_recent_preset_pressed).bind(btn_preset_new));
697}
698
699void ColorPicker::_show_hide_preset(const bool &p_is_btn_pressed, Button *p_btn_preset, Container *p_preset_container) {
700 if (p_is_btn_pressed) {
701 p_preset_container->show();
702 } else {
703 p_preset_container->hide();
704 }
705 _update_drop_down_arrow(p_is_btn_pressed, p_btn_preset);
706}
707
708void ColorPicker::_update_drop_down_arrow(const bool &p_is_btn_pressed, Button *p_btn_preset) {
709 if (p_is_btn_pressed) {
710 p_btn_preset->set_icon(theme_cache.expanded_arrow);
711 } else {
712 p_btn_preset->set_icon(theme_cache.folded_arrow);
713 }
714}
715
716void ColorPicker::_set_mode_popup_value(ColorModeType p_mode) {
717 ERR_FAIL_INDEX(p_mode, MODE_MAX + 1);
718
719 if (p_mode == MODE_MAX) {
720 set_colorize_sliders(!colorize_sliders);
721 } else {
722 set_color_mode(p_mode);
723 }
724}
725
726Variant ColorPicker::_get_drag_data_fw(const Point2 &p_point, Control *p_from_control) {
727 ColorPresetButton *dragged_preset_button = Object::cast_to<ColorPresetButton>(p_from_control);
728
729 if (!dragged_preset_button) {
730 return Variant();
731 }
732
733 ColorPresetButton *drag_preview = memnew(ColorPresetButton(dragged_preset_button->get_preset_color(), _get_preset_size()));
734 set_drag_preview(drag_preview);
735
736 Dictionary drag_data;
737 drag_data["type"] = "color_preset";
738 drag_data["color_preset"] = dragged_preset_button->get_index();
739
740 return drag_data;
741}
742
743bool ColorPicker::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const {
744 Dictionary d = p_data;
745 if (!d.has("type") || String(d["type"]) != "color_preset") {
746 return false;
747 }
748 return true;
749}
750
751void ColorPicker::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) {
752 Dictionary d = p_data;
753 if (!d.has("type")) {
754 return;
755 }
756
757 if (String(d["type"]) == "color_preset") {
758 int preset_from_id = d["color_preset"];
759 int hover_now = p_from_control->get_index();
760
761 if (preset_from_id == hover_now || hover_now == -1) {
762 return;
763 }
764 preset_container->move_child(preset_container->get_child(preset_from_id), hover_now);
765 }
766}
767
768void ColorPicker::add_preset(const Color &p_color) {
769 List<Color>::Element *e = presets.find(p_color);
770 if (e) {
771 presets.move_to_back(e);
772 preset_cache.move_to_back(preset_cache.find(p_color));
773
774 preset_container->move_child(preset_group->get_pressed_button(), preset_container->get_child_count() - 1);
775 } else {
776 presets.push_back(p_color);
777 preset_cache.push_back(p_color);
778
779 _add_preset_button(_get_preset_size(), p_color);
780 }
781
782#ifdef TOOLS_ENABLED
783 if (editor_settings) {
784 PackedColorArray arr_to_save = get_presets();
785 editor_settings->call(SNAME("set_project_metadata"), "color_picker", "presets", arr_to_save);
786 }
787#endif
788}
789
790void ColorPicker::add_recent_preset(const Color &p_color) {
791 if (!_select_from_recent_preset_hbc(p_color)) {
792 if (recent_preset_hbc->get_child_count() >= PRESET_COLUMN_COUNT) {
793 recent_preset_cache.pop_front();
794 recent_presets.pop_front();
795 recent_preset_hbc->get_child(PRESET_COLUMN_COUNT - 1)->queue_free();
796 }
797 recent_presets.push_back(p_color);
798 recent_preset_cache.push_back(p_color);
799 _add_recent_preset_button(_get_preset_size(), p_color);
800 }
801 _select_from_preset_container(p_color);
802
803#ifdef TOOLS_ENABLED
804 if (editor_settings) {
805 PackedColorArray arr_to_save = get_recent_presets();
806 editor_settings->call(SNAME("set_project_metadata"), "color_picker", "recent_presets", arr_to_save);
807 }
808#endif
809}
810
811void ColorPicker::erase_preset(const Color &p_color) {
812 List<Color>::Element *e = presets.find(p_color);
813 if (e) {
814 presets.erase(e);
815 preset_cache.erase(preset_cache.find(p_color));
816
817 // Find preset button to remove.
818 for (int i = 1; i < preset_container->get_child_count(); i++) {
819 ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(preset_container->get_child(i));
820 if (current_btn && p_color == current_btn->get_preset_color()) {
821 current_btn->queue_free();
822 break;
823 }
824 }
825
826#ifdef TOOLS_ENABLED
827 if (editor_settings) {
828 PackedColorArray arr_to_save = get_presets();
829 editor_settings->call(SNAME("set_project_metadata"), "color_picker", "presets", arr_to_save);
830 }
831#endif
832 }
833}
834
835void ColorPicker::erase_recent_preset(const Color &p_color) {
836 List<Color>::Element *e = recent_presets.find(p_color);
837 if (e) {
838 recent_presets.erase(e);
839 recent_preset_cache.erase(recent_preset_cache.find(p_color));
840
841 // Find recent preset button to remove.
842 for (int i = 1; i < recent_preset_hbc->get_child_count(); i++) {
843 ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(recent_preset_hbc->get_child(i));
844 if (current_btn && p_color == current_btn->get_preset_color()) {
845 current_btn->queue_free();
846 break;
847 }
848 }
849
850#ifdef TOOLS_ENABLED
851 if (editor_settings) {
852 PackedColorArray arr_to_save = get_recent_presets();
853 editor_settings->call(SNAME("set_project_metadata"), "color_picker", "recent_presets", arr_to_save);
854 }
855#endif
856 }
857}
858
859PackedColorArray ColorPicker::get_presets() const {
860 PackedColorArray arr;
861 arr.resize(presets.size());
862 for (int i = 0; i < presets.size(); i++) {
863 arr.set(i, presets[i]);
864 }
865 return arr;
866}
867
868PackedColorArray ColorPicker::get_recent_presets() const {
869 PackedColorArray arr;
870 arr.resize(recent_presets.size());
871 for (int i = 0; i < recent_presets.size(); i++) {
872 arr.set(i, recent_presets[i]);
873 }
874 return arr;
875}
876
877void ColorPicker::set_color_mode(ColorModeType p_mode) {
878 ERR_FAIL_INDEX(p_mode, MODE_MAX);
879
880 if (current_mode == p_mode) {
881 return;
882 }
883
884 if (slider_theme_modified) {
885 _reset_sliders_theme();
886 }
887
888 mode_popup->set_item_checked(current_mode, false);
889 mode_popup->set_item_checked(p_mode, true);
890
891 if (p_mode < MODE_BUTTON_COUNT) {
892 mode_btns[p_mode]->set_pressed(true);
893 } else if (current_mode < MODE_BUTTON_COUNT) {
894 mode_btns[current_mode]->set_pressed(false);
895 }
896
897 current_mode = p_mode;
898
899 if (!is_inside_tree()) {
900 return;
901 }
902
903 _update_controls();
904 _update_color();
905}
906
907ColorPicker::ColorModeType ColorPicker::get_color_mode() const {
908 return current_mode;
909}
910
911void ColorPicker::set_colorize_sliders(bool p_colorize_sliders) {
912 if (colorize_sliders == p_colorize_sliders) {
913 return;
914 }
915
916 colorize_sliders = p_colorize_sliders;
917 mode_popup->set_item_checked(MODE_MAX + 1, colorize_sliders);
918
919 if (colorize_sliders) {
920 Ref<StyleBoxEmpty> style_box_empty(memnew(StyleBoxEmpty));
921
922 if (!slider_theme_modified) {
923 for (int i = 0; i < SLIDER_COUNT; i++) {
924 sliders[i]->add_theme_style_override("slider", style_box_empty);
925 }
926 }
927 alpha_slider->add_theme_style_override("slider", style_box_empty);
928 } else {
929 Ref<StyleBoxFlat> style_box_flat(memnew(StyleBoxFlat));
930 style_box_flat->set_content_margin(SIDE_TOP, 16 * theme_cache.base_scale);
931 style_box_flat->set_bg_color(Color(0.2, 0.23, 0.31).lerp(Color(0, 0, 0, 1), 0.3).clamp());
932
933 if (!slider_theme_modified) {
934 for (int i = 0; i < SLIDER_COUNT; i++) {
935 sliders[i]->add_theme_style_override("slider", style_box_flat);
936 }
937 }
938 alpha_slider->add_theme_style_override("slider", style_box_flat);
939 }
940}
941
942bool ColorPicker::is_colorizing_sliders() const {
943 return colorize_sliders;
944}
945
946void ColorPicker::set_deferred_mode(bool p_enabled) {
947 deferred_mode_enabled = p_enabled;
948}
949
950bool ColorPicker::is_deferred_mode() const {
951 return deferred_mode_enabled;
952}
953
954void ColorPicker::_update_text_value() {
955 bool text_visible = true;
956 if (text_is_constructor) {
957 String t = "Color(" + String::num(color.r, 3) + ", " + String::num(color.g, 3) + ", " + String::num(color.b, 3);
958 if (edit_alpha && color.a < 1) {
959 t += ", " + String::num(color.a, 3) + ")";
960 } else {
961 t += ")";
962 }
963 c_text->set_text(t);
964 }
965
966 if (color.r > 1 || color.g > 1 || color.b > 1 || color.r < 0 || color.g < 0 || color.b < 0) {
967 text_visible = false;
968 } else if (!text_is_constructor) {
969 c_text->set_text(color.to_html(edit_alpha && color.a < 1));
970 }
971
972 text_type->set_visible(text_visible);
973 c_text->set_visible(text_visible);
974}
975
976void ColorPicker::_sample_input(const Ref<InputEvent> &p_event) {
977 const Ref<InputEventMouseButton> mb = p_event;
978 if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
979 const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
980 if (rect_old.has_point(mb->get_position())) {
981 // Revert to the old color when left-clicking the old color sample.
982 set_pick_color(old_color);
983 emit_signal(SNAME("color_changed"), color);
984 }
985 }
986}
987
988void ColorPicker::_sample_draw() {
989 // Covers the right half of the sample if the old color is being displayed,
990 // or the whole sample if it's not being displayed.
991 Rect2 rect_new;
992
993 if (display_old_color) {
994 rect_new = Rect2(Point2(sample->get_size().width * 0.5, 0), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
995
996 // Draw both old and new colors for easier comparison (only if spawned from a ColorPickerButton).
997 const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
998
999 if (old_color.a < 1.0) {
1000 sample->draw_texture_rect(theme_cache.sample_bg, rect_old, true);
1001 }
1002
1003 sample->draw_rect(rect_old, old_color);
1004
1005 if (old_color.r > 1 || old_color.g > 1 || old_color.b > 1) {
1006 // Draw an indicator to denote that the old color is "overbright" and can't be displayed accurately in the preview.
1007 sample->draw_texture(theme_cache.overbright_indicator, Point2());
1008 }
1009 } else {
1010 rect_new = Rect2(Point2(), Size2(sample->get_size().width, sample->get_size().height * 0.95));
1011 }
1012
1013 if (color.a < 1.0) {
1014 sample->draw_texture_rect(theme_cache.sample_bg, rect_new, true);
1015 }
1016
1017 sample->draw_rect(rect_new, color);
1018
1019 if (color.r > 1 || color.g > 1 || color.b > 1) {
1020 // Draw an indicator to denote that the new color is "overbright" and can't be displayed accurately in the preview.
1021 sample->draw_texture(theme_cache.overbright_indicator, Point2(uv_edit->get_size().width * 0.5, 0));
1022 }
1023}
1024
1025void ColorPicker::_hsv_draw(int p_which, Control *c) {
1026 if (!c) {
1027 return;
1028 }
1029
1030 PickerShapeType actual_shape = _get_actual_shape();
1031 if (p_which == 0) {
1032 Vector<Point2> points;
1033 Vector<Color> colors;
1034 Vector<Color> colors2;
1035 Color col = color;
1036 Vector2 center = c->get_size() / 2.0;
1037
1038 switch (actual_shape) {
1039 case SHAPE_HSV_WHEEL: {
1040 points.resize(4);
1041 colors.resize(4);
1042 colors2.resize(4);
1043 real_t ring_radius_x = Math_SQRT12 * c->get_size().width * 0.42;
1044 real_t ring_radius_y = Math_SQRT12 * c->get_size().height * 0.42;
1045
1046 points.set(0, center - Vector2(ring_radius_x, ring_radius_y));
1047 points.set(1, center + Vector2(ring_radius_x, -ring_radius_y));
1048 points.set(2, center + Vector2(ring_radius_x, ring_radius_y));
1049 points.set(3, center + Vector2(-ring_radius_x, ring_radius_y));
1050 colors.set(0, Color(1, 1, 1, 1));
1051 colors.set(1, Color(1, 1, 1, 1));
1052 colors.set(2, Color(0, 0, 0, 1));
1053 colors.set(3, Color(0, 0, 0, 1));
1054 c->draw_polygon(points, colors);
1055
1056 col.set_hsv(h, 1, 1);
1057 col.a = 0;
1058 colors2.set(0, col);
1059 col.a = 1;
1060 colors2.set(1, col);
1061 col.set_hsv(h, 1, 0);
1062 colors2.set(2, col);
1063 col.a = 0;
1064 colors2.set(3, col);
1065 c->draw_polygon(points, colors2);
1066 break;
1067 }
1068 case SHAPE_HSV_RECTANGLE: {
1069 points.resize(4);
1070 colors.resize(4);
1071 colors2.resize(4);
1072 points.set(0, Vector2());
1073 points.set(1, Vector2(c->get_size().x, 0));
1074 points.set(2, c->get_size());
1075 points.set(3, Vector2(0, c->get_size().y));
1076 colors.set(0, Color(1, 1, 1, 1));
1077 colors.set(1, Color(1, 1, 1, 1));
1078 colors.set(2, Color(0, 0, 0, 1));
1079 colors.set(3, Color(0, 0, 0, 1));
1080 c->draw_polygon(points, colors);
1081 col = color;
1082 col.set_hsv(h, 1, 1);
1083 col.a = 0;
1084 colors2.set(0, col);
1085 col.a = 1;
1086 colors2.set(1, col);
1087 col.set_hsv(h, 1, 0);
1088 colors2.set(2, col);
1089 col.a = 0;
1090 colors2.set(3, col);
1091 c->draw_polygon(points, colors2);
1092 break;
1093 }
1094 default: {
1095 }
1096 }
1097
1098 int x;
1099 int y;
1100 if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
1101 x = center.x + (center.x * Math::cos(h * Math_TAU) * s) - (theme_cache.picker_cursor->get_width() / 2);
1102 y = center.y + (center.y * Math::sin(h * Math_TAU) * s) - (theme_cache.picker_cursor->get_height() / 2);
1103 } else {
1104 real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0;
1105 real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0;
1106
1107 Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2);
1108 x = CLAMP(real_size.x * s, 0, real_size.x) + corner_x - (theme_cache.picker_cursor->get_width() / 2);
1109 y = CLAMP(real_size.y - real_size.y * v, 0, real_size.y) + corner_y - (theme_cache.picker_cursor->get_height() / 2);
1110 }
1111 c->draw_texture(theme_cache.picker_cursor, Point2(x, y));
1112
1113 col.set_hsv(h, 1, 1);
1114 if (actual_shape == SHAPE_HSV_WHEEL) {
1115 points.resize(4);
1116 double h1 = h - (0.5 / 360);
1117 double h2 = h + (0.5 / 360);
1118 points.set(0, Point2(center.x + (center.x * Math::cos(h1 * Math_TAU)), center.y + (center.y * Math::sin(h1 * Math_TAU))));
1119 points.set(1, Point2(center.x + (center.x * Math::cos(h1 * Math_TAU) * 0.84), center.y + (center.y * Math::sin(h1 * Math_TAU) * 0.84)));
1120 points.set(2, Point2(center.x + (center.x * Math::cos(h2 * Math_TAU)), center.y + (center.y * Math::sin(h2 * Math_TAU))));
1121 points.set(3, Point2(center.x + (center.x * Math::cos(h2 * Math_TAU) * 0.84), center.y + (center.y * Math::sin(h2 * Math_TAU) * 0.84)));
1122 c->draw_multiline(points, col.inverted());
1123 }
1124
1125 } else if (p_which == 1) {
1126 if (actual_shape == SHAPE_HSV_RECTANGLE) {
1127 c->draw_set_transform(Point2(), -Math_PI / 2, Size2(c->get_size().x, -c->get_size().y));
1128 c->draw_texture_rect(theme_cache.color_hue, Rect2(Point2(), Size2(1, 1)));
1129 c->draw_set_transform(Point2(), 0, Size2(1, 1));
1130 int y = c->get_size().y - c->get_size().y * (1.0 - h);
1131 Color col;
1132 col.set_hsv(h, 1, 1);
1133 c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted());
1134 } else if (actual_shape == SHAPE_OKHSL_CIRCLE) {
1135 Vector<Point2> points;
1136 Vector<Color> colors;
1137 Color col;
1138 col.set_ok_hsl(h, s, 1);
1139 Color col2;
1140 col2.set_ok_hsl(h, s, 0.5);
1141 Color col3;
1142 col3.set_ok_hsl(h, s, 0);
1143 points.resize(6);
1144 colors.resize(6);
1145 points.set(0, Vector2(c->get_size().x, 0));
1146 points.set(1, Vector2(c->get_size().x, c->get_size().y * 0.5));
1147 points.set(2, c->get_size());
1148 points.set(3, Vector2(0, c->get_size().y));
1149 points.set(4, Vector2(0, c->get_size().y * 0.5));
1150 points.set(5, Vector2());
1151 colors.set(0, col);
1152 colors.set(1, col2);
1153 colors.set(2, col3);
1154 colors.set(3, col3);
1155 colors.set(4, col2);
1156 colors.set(5, col);
1157 c->draw_polygon(points, colors);
1158 int y = c->get_size().y - c->get_size().y * CLAMP(v, 0, 1);
1159 col.set_ok_hsl(h, 1, v);
1160 c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted());
1161 } else if (actual_shape == SHAPE_VHS_CIRCLE) {
1162 Vector<Point2> points;
1163 Vector<Color> colors;
1164 Color col;
1165 col.set_hsv(h, s, 1);
1166 points.resize(4);
1167 colors.resize(4);
1168 points.set(0, Vector2());
1169 points.set(1, Vector2(c->get_size().x, 0));
1170 points.set(2, c->get_size());
1171 points.set(3, Vector2(0, c->get_size().y));
1172 colors.set(0, col);
1173 colors.set(1, col);
1174 colors.set(2, Color(0, 0, 0));
1175 colors.set(3, Color(0, 0, 0));
1176 c->draw_polygon(points, colors);
1177 int y = c->get_size().y - c->get_size().y * CLAMP(v, 0, 1);
1178 col.set_hsv(h, 1, v);
1179 c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted());
1180 }
1181 } else if (p_which == 2) {
1182 c->draw_rect(Rect2(Point2(), c->get_size()), Color(1, 1, 1));
1183 if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
1184 circle_mat->set_shader_parameter("v", v);
1185 }
1186 }
1187}
1188
1189void ColorPicker::_slider_draw(int p_which) {
1190 if (colorize_sliders) {
1191 modes[current_mode]->slider_draw(p_which);
1192 }
1193}
1194
1195void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
1196 Ref<InputEventMouseButton> bev = p_event;
1197 PickerShapeType actual_shape = _get_actual_shape();
1198
1199 if (bev.is_valid()) {
1200 if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
1201 Vector2 center = c->get_size() / 2.0;
1202 if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
1203 real_t dist = center.distance_to(bev->get_position());
1204 if (dist <= center.x) {
1205 real_t rad = center.angle_to_point(bev->get_position());
1206 h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU;
1207 s = CLAMP(dist / center.x, 0, 1);
1208 } else {
1209 return;
1210 }
1211 } else {
1212 real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0;
1213 real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0;
1214 Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2);
1215
1216 if (bev->get_position().x < corner_x || bev->get_position().x > c->get_size().x - corner_x ||
1217 bev->get_position().y < corner_y || bev->get_position().y > c->get_size().y - corner_y) {
1218 {
1219 real_t dist = center.distance_to(bev->get_position());
1220
1221 if (dist >= center.x * 0.84 && dist <= center.x) {
1222 real_t rad = center.angle_to_point(bev->get_position());
1223 h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU;
1224 spinning = true;
1225 } else {
1226 return;
1227 }
1228 }
1229 }
1230
1231 if (!spinning) {
1232 real_t x = CLAMP(bev->get_position().x - corner_x, 0, real_size.x);
1233 real_t y = CLAMP(bev->get_position().y - corner_y, 0, real_size.y);
1234
1235 s = x / real_size.x;
1236 v = 1.0 - y / real_size.y;
1237 }
1238 }
1239
1240 changing_color = true;
1241
1242 _copy_hsv_to_color();
1243 last_color = color;
1244 set_pick_color(color);
1245 _update_color();
1246
1247 if (!deferred_mode_enabled) {
1248 emit_signal(SNAME("color_changed"), color);
1249 }
1250 } else if (!bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
1251 if (deferred_mode_enabled) {
1252 emit_signal(SNAME("color_changed"), color);
1253 }
1254 add_recent_preset(color);
1255 changing_color = false;
1256 spinning = false;
1257 } else {
1258 changing_color = false;
1259 spinning = false;
1260 }
1261 }
1262
1263 Ref<InputEventMouseMotion> mev = p_event;
1264
1265 if (mev.is_valid()) {
1266 if (!changing_color) {
1267 return;
1268 }
1269
1270 Vector2 center = c->get_size() / 2.0;
1271 if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
1272 real_t dist = center.distance_to(mev->get_position());
1273 real_t rad = center.angle_to_point(mev->get_position());
1274 h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU;
1275 s = CLAMP(dist / center.x, 0, 1);
1276 } else {
1277 if (spinning) {
1278 real_t rad = center.angle_to_point(mev->get_position());
1279 h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU;
1280 } else {
1281 real_t corner_x = (c == wheel_uv) ? center.x - Math_SQRT12 * c->get_size().width * 0.42 : 0;
1282 real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0;
1283 Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2);
1284
1285 real_t x = CLAMP(mev->get_position().x - corner_x, 0, real_size.x);
1286 real_t y = CLAMP(mev->get_position().y - corner_y, 0, real_size.y);
1287
1288 s = x / real_size.x;
1289 v = 1.0 - y / real_size.y;
1290 }
1291 }
1292
1293 _copy_hsv_to_color();
1294 last_color = color;
1295 set_pick_color(color);
1296 _update_color();
1297
1298 if (!deferred_mode_enabled) {
1299 emit_signal(SNAME("color_changed"), color);
1300 }
1301 }
1302}
1303
1304void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
1305 Ref<InputEventMouseButton> bev = p_event;
1306 PickerShapeType actual_shape = _get_actual_shape();
1307
1308 if (bev.is_valid()) {
1309 if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
1310 changing_color = true;
1311 float y = CLAMP((float)bev->get_position().y, 0, w_edit->get_size().height);
1312 if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
1313 v = 1.0 - (y / w_edit->get_size().height);
1314 } else {
1315 h = y / w_edit->get_size().height;
1316 }
1317 } else {
1318 changing_color = false;
1319 }
1320
1321 _copy_hsv_to_color();
1322 last_color = color;
1323 set_pick_color(color);
1324 _update_color();
1325
1326 if (!bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
1327 add_recent_preset(color);
1328 emit_signal(SNAME("color_changed"), color);
1329 } else if (!deferred_mode_enabled) {
1330 emit_signal(SNAME("color_changed"), color);
1331 }
1332 }
1333
1334 Ref<InputEventMouseMotion> mev = p_event;
1335
1336 if (mev.is_valid()) {
1337 if (!changing_color) {
1338 return;
1339 }
1340 float y = CLAMP((float)mev->get_position().y, 0, w_edit->get_size().height);
1341 if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
1342 v = 1.0 - (y / w_edit->get_size().height);
1343 } else {
1344 h = y / w_edit->get_size().height;
1345 }
1346
1347 _copy_hsv_to_color();
1348 last_color = color;
1349 set_pick_color(color);
1350 _update_color();
1351
1352 if (!deferred_mode_enabled) {
1353 emit_signal(SNAME("color_changed"), color);
1354 }
1355 }
1356}
1357
1358void ColorPicker::_slider_or_spin_input(const Ref<InputEvent> &p_event) {
1359 if (line_edit_mouse_release) {
1360 line_edit_mouse_release = false;
1361 return;
1362 }
1363 Ref<InputEventMouseButton> bev = p_event;
1364 if (bev.is_valid() && !bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
1365 add_recent_preset(color);
1366 }
1367}
1368
1369void ColorPicker::_line_edit_input(const Ref<InputEvent> &p_event) {
1370 Ref<InputEventMouseButton> bev = p_event;
1371 if (bev.is_valid() && !bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
1372 line_edit_mouse_release = true;
1373 }
1374}
1375
1376void ColorPicker::_preset_input(const Ref<InputEvent> &p_event, const Color &p_color) {
1377 Ref<InputEventMouseButton> bev = p_event;
1378
1379 if (bev.is_valid()) {
1380 if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
1381 set_pick_color(p_color);
1382 add_recent_preset(color);
1383 emit_signal(SNAME("color_changed"), p_color);
1384 } else if (bev->is_pressed() && bev->get_button_index() == MouseButton::RIGHT && can_add_swatches) {
1385 erase_preset(p_color);
1386 emit_signal(SNAME("preset_removed"), p_color);
1387 }
1388 }
1389}
1390
1391void ColorPicker::_recent_preset_pressed(const bool p_pressed, ColorPresetButton *p_preset) {
1392 if (!p_pressed) {
1393 return;
1394 }
1395 set_pick_color(p_preset->get_preset_color());
1396
1397 recent_presets.move_to_back(recent_presets.find(p_preset->get_preset_color()));
1398 List<Color>::Element *e = recent_preset_cache.find(p_preset->get_preset_color());
1399 if (e) {
1400 recent_preset_cache.move_to_back(e);
1401 }
1402
1403 recent_preset_hbc->move_child(p_preset, 0);
1404 emit_signal(SNAME("color_changed"), p_preset->get_preset_color());
1405}
1406
1407void ColorPicker::_text_changed(const String &) {
1408 text_changed = true;
1409}
1410
1411void ColorPicker::_add_preset_pressed() {
1412 add_preset(color);
1413 emit_signal(SNAME("preset_added"), color);
1414}
1415
1416void ColorPicker::_pick_button_pressed() {
1417 is_picking_color = true;
1418 set_process_internal(true);
1419
1420 if (!picker_window) {
1421 picker_window = memnew(Popup);
1422 picker_window->set_size(Vector2i(1, 1));
1423 picker_window->connect("visibility_changed", callable_mp(this, &ColorPicker::_pick_finished));
1424 add_child(picker_window, false, INTERNAL_MODE_FRONT);
1425 }
1426 picker_window->popup();
1427}
1428
1429void ColorPicker::_pick_finished() {
1430 if (picker_window->is_visible()) {
1431 return;
1432 }
1433
1434 if (Input::get_singleton()->is_key_pressed(Key::ESCAPE)) {
1435 set_pick_color(old_color);
1436 } else {
1437 emit_signal(SNAME("color_changed"), color);
1438 }
1439 is_picking_color = false;
1440 set_process_internal(false);
1441 picker_window->hide();
1442}
1443
1444void ColorPicker::_pick_button_pressed_legacy() {
1445 if (!is_inside_tree()) {
1446 return;
1447 }
1448
1449 if (!picker_window) {
1450 picker_window = memnew(Popup);
1451 picker_window->hide();
1452 picker_window->set_transient(true);
1453 add_child(picker_window, false, INTERNAL_MODE_FRONT);
1454
1455 picker_texture_rect = memnew(TextureRect);
1456 picker_texture_rect->set_anchors_preset(Control::PRESET_FULL_RECT);
1457 picker_window->add_child(picker_texture_rect);
1458 picker_texture_rect->set_default_cursor_shape(CURSOR_POINTING_HAND);
1459 picker_texture_rect->connect("gui_input", callable_mp(this, &ColorPicker::_picker_texture_input));
1460
1461 picker_preview = memnew(Panel);
1462 picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
1463 picker_preview->set_mouse_filter(MOUSE_FILTER_IGNORE);
1464 picker_window->add_child(picker_preview);
1465
1466 picker_preview_label = memnew(Label);
1467 picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
1468 picker_preview_label->set_text("Color Picking active");
1469 picker_preview->add_child(picker_preview_label);
1470
1471 picker_preview_style_box = (Ref<StyleBoxFlat>)memnew(StyleBoxFlat);
1472 picker_preview_style_box->set_bg_color(Color(1.0, 1.0, 1.0));
1473 picker_preview->add_theme_style_override("panel", picker_preview_style_box);
1474 }
1475
1476 Rect2i screen_rect;
1477 if (picker_window->is_embedded()) {
1478 screen_rect = picker_window->get_embedder()->get_visible_rect();
1479 picker_window->set_position(Point2i());
1480 picker_texture_rect->set_texture(ImageTexture::create_from_image(picker_window->get_embedder()->get_texture()->get_image()));
1481 } else {
1482 screen_rect = picker_window->get_parent_rect();
1483 picker_window->set_position(screen_rect.position);
1484
1485 Ref<Image> target_image = Image::create_empty(screen_rect.size.x, screen_rect.size.y, false, Image::FORMAT_RGB8);
1486 DisplayServer *ds = DisplayServer::get_singleton();
1487
1488 // Add the Texture of each Window to the Image.
1489 Vector<DisplayServer::WindowID> wl = ds->get_window_list();
1490 // FIXME: sort windows by visibility.
1491 for (int index = 0; index < wl.size(); index++) {
1492 DisplayServer::WindowID wid = wl[index];
1493 if (wid == DisplayServer::INVALID_WINDOW_ID) {
1494 continue;
1495 }
1496
1497 ObjectID woid = DisplayServer::get_singleton()->window_get_attached_instance_id(wid);
1498 if (woid == ObjectID()) {
1499 continue;
1500 }
1501
1502 Window *w = Object::cast_to<Window>(ObjectDB::get_instance(woid));
1503 Ref<Image> img = w->get_texture()->get_image();
1504 if (!img.is_valid() || img->is_empty()) {
1505 continue;
1506 }
1507 img->convert(Image::FORMAT_RGB8);
1508 target_image->blit_rect(img, Rect2i(Point2i(0, 0), img->get_size()), w->get_position());
1509 }
1510
1511 picker_texture_rect->set_texture(ImageTexture::create_from_image(target_image));
1512 }
1513
1514 picker_window->set_size(screen_rect.size);
1515 picker_preview->set_size(screen_rect.size / 10.0); // 10% of size in each axis.
1516 picker_window->popup();
1517}
1518
1519void ColorPicker::_picker_texture_input(const Ref<InputEvent> &p_event) {
1520 if (!is_inside_tree()) {
1521 return;
1522 }
1523
1524 Ref<InputEventMouseButton> bev = p_event;
1525 if (bev.is_valid() && bev->get_button_index() == MouseButton::LEFT && !bev->is_pressed()) {
1526 set_pick_color(picker_color);
1527 emit_signal(SNAME("color_changed"), color);
1528 picker_window->hide();
1529 }
1530
1531 Ref<InputEventMouseMotion> mev = p_event;
1532 if (mev.is_valid()) {
1533 Ref<Image> img = picker_texture_rect->get_texture()->get_image();
1534 if (img.is_valid() && !img->is_empty()) {
1535 Vector2 ofs = mev->get_position();
1536 picker_color = img->get_pixel(ofs.x, ofs.y);
1537 picker_preview_style_box->set_bg_color(picker_color);
1538 picker_preview_label->set_self_modulate(picker_color.get_luminance() < 0.5 ? Color(1.0f, 1.0f, 1.0f) : Color(0.0f, 0.0f, 0.0f));
1539 }
1540 }
1541}
1542
1543void ColorPicker::_html_focus_exit() {
1544 if (c_text->is_menu_visible()) {
1545 return;
1546 }
1547 _html_submitted(c_text->get_text());
1548}
1549
1550void ColorPicker::set_can_add_swatches(bool p_enabled) {
1551 if (can_add_swatches == p_enabled) {
1552 return;
1553 }
1554 can_add_swatches = p_enabled;
1555 if (!p_enabled) {
1556 btn_add_preset->set_disabled(true);
1557 btn_add_preset->set_focus_mode(FOCUS_NONE);
1558 } else {
1559 btn_add_preset->set_disabled(false);
1560 btn_add_preset->set_focus_mode(FOCUS_ALL);
1561 }
1562}
1563
1564bool ColorPicker::are_swatches_enabled() const {
1565 return can_add_swatches;
1566}
1567
1568void ColorPicker::set_presets_visible(bool p_visible) {
1569 if (presets_visible == p_visible) {
1570 return;
1571 }
1572 presets_visible = p_visible;
1573 btn_preset->set_visible(p_visible);
1574 btn_recent_preset->set_visible(p_visible);
1575}
1576
1577bool ColorPicker::are_presets_visible() const {
1578 return presets_visible;
1579}
1580
1581void ColorPicker::set_modes_visible(bool p_visible) {
1582 if (color_modes_visible == p_visible) {
1583 return;
1584 }
1585 color_modes_visible = p_visible;
1586 mode_hbc->set_visible(p_visible);
1587}
1588
1589bool ColorPicker::are_modes_visible() const {
1590 return color_modes_visible;
1591}
1592
1593void ColorPicker::set_sampler_visible(bool p_visible) {
1594 if (sampler_visible == p_visible) {
1595 return;
1596 }
1597 sampler_visible = p_visible;
1598 sample_hbc->set_visible(p_visible);
1599}
1600
1601bool ColorPicker::is_sampler_visible() const {
1602 return sampler_visible;
1603}
1604
1605void ColorPicker::set_sliders_visible(bool p_visible) {
1606 if (sliders_visible == p_visible) {
1607 return;
1608 }
1609 sliders_visible = p_visible;
1610 slider_gc->set_visible(p_visible);
1611}
1612
1613bool ColorPicker::are_sliders_visible() const {
1614 return sliders_visible;
1615}
1616
1617void ColorPicker::set_hex_visible(bool p_visible) {
1618 if (hex_visible == p_visible) {
1619 return;
1620 }
1621 hex_visible = p_visible;
1622 hex_hbc->set_visible(p_visible);
1623}
1624
1625bool ColorPicker::is_hex_visible() const {
1626 return hex_visible;
1627}
1628
1629void ColorPicker::_bind_methods() {
1630 ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color);
1631 ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color);
1632 ClassDB::bind_method(D_METHOD("set_deferred_mode", "mode"), &ColorPicker::set_deferred_mode);
1633 ClassDB::bind_method(D_METHOD("is_deferred_mode"), &ColorPicker::is_deferred_mode);
1634 ClassDB::bind_method(D_METHOD("set_color_mode", "color_mode"), &ColorPicker::set_color_mode);
1635 ClassDB::bind_method(D_METHOD("get_color_mode"), &ColorPicker::get_color_mode);
1636 ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPicker::set_edit_alpha);
1637 ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPicker::is_editing_alpha);
1638 ClassDB::bind_method(D_METHOD("set_can_add_swatches", "enabled"), &ColorPicker::set_can_add_swatches);
1639 ClassDB::bind_method(D_METHOD("are_swatches_enabled"), &ColorPicker::are_swatches_enabled);
1640 ClassDB::bind_method(D_METHOD("set_presets_visible", "visible"), &ColorPicker::set_presets_visible);
1641 ClassDB::bind_method(D_METHOD("are_presets_visible"), &ColorPicker::are_presets_visible);
1642 ClassDB::bind_method(D_METHOD("set_modes_visible", "visible"), &ColorPicker::set_modes_visible);
1643 ClassDB::bind_method(D_METHOD("are_modes_visible"), &ColorPicker::are_modes_visible);
1644 ClassDB::bind_method(D_METHOD("set_sampler_visible", "visible"), &ColorPicker::set_sampler_visible);
1645 ClassDB::bind_method(D_METHOD("is_sampler_visible"), &ColorPicker::is_sampler_visible);
1646 ClassDB::bind_method(D_METHOD("set_sliders_visible", "visible"), &ColorPicker::set_sliders_visible);
1647 ClassDB::bind_method(D_METHOD("are_sliders_visible"), &ColorPicker::are_sliders_visible);
1648 ClassDB::bind_method(D_METHOD("set_hex_visible", "visible"), &ColorPicker::set_hex_visible);
1649 ClassDB::bind_method(D_METHOD("is_hex_visible"), &ColorPicker::is_hex_visible);
1650 ClassDB::bind_method(D_METHOD("add_preset", "color"), &ColorPicker::add_preset);
1651 ClassDB::bind_method(D_METHOD("erase_preset", "color"), &ColorPicker::erase_preset);
1652 ClassDB::bind_method(D_METHOD("get_presets"), &ColorPicker::get_presets);
1653 ClassDB::bind_method(D_METHOD("add_recent_preset", "color"), &ColorPicker::add_recent_preset);
1654 ClassDB::bind_method(D_METHOD("erase_recent_preset", "color"), &ColorPicker::erase_recent_preset);
1655 ClassDB::bind_method(D_METHOD("get_recent_presets"), &ColorPicker::get_recent_presets);
1656 ClassDB::bind_method(D_METHOD("set_picker_shape", "shape"), &ColorPicker::set_picker_shape);
1657 ClassDB::bind_method(D_METHOD("get_picker_shape"), &ColorPicker::get_picker_shape);
1658
1659 ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color");
1660 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
1661 ADD_PROPERTY(PropertyInfo(Variant::INT, "color_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW,OKHSL"), "set_color_mode", "get_color_mode");
1662 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode");
1663 ADD_PROPERTY(PropertyInfo(Variant::INT, "picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle,OKHSL Circle,None"), "set_picker_shape", "get_picker_shape");
1664 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_add_swatches"), "set_can_add_swatches", "are_swatches_enabled");
1665 ADD_GROUP("Customization", "");
1666 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sampler_visible"), "set_sampler_visible", "is_sampler_visible");
1667 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "color_modes_visible"), "set_modes_visible", "are_modes_visible");
1668 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sliders_visible"), "set_sliders_visible", "are_sliders_visible");
1669 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hex_visible"), "set_hex_visible", "is_hex_visible");
1670 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "presets_visible"), "set_presets_visible", "are_presets_visible");
1671
1672 ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
1673 ADD_SIGNAL(MethodInfo("preset_added", PropertyInfo(Variant::COLOR, "color")));
1674 ADD_SIGNAL(MethodInfo("preset_removed", PropertyInfo(Variant::COLOR, "color")));
1675
1676 BIND_ENUM_CONSTANT(MODE_RGB);
1677 BIND_ENUM_CONSTANT(MODE_HSV);
1678 BIND_ENUM_CONSTANT(MODE_RAW);
1679 BIND_ENUM_CONSTANT(MODE_OKHSL);
1680
1681 BIND_ENUM_CONSTANT(SHAPE_HSV_RECTANGLE);
1682 BIND_ENUM_CONSTANT(SHAPE_HSV_WHEEL);
1683 BIND_ENUM_CONSTANT(SHAPE_VHS_CIRCLE);
1684 BIND_ENUM_CONSTANT(SHAPE_OKHSL_CIRCLE);
1685 BIND_ENUM_CONSTANT(SHAPE_NONE);
1686
1687 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, ColorPicker, content_margin, "margin");
1688 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, ColorPicker, label_width);
1689
1690 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, ColorPicker, sv_width);
1691 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, ColorPicker, sv_height);
1692 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, ColorPicker, h_width);
1693
1694 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, ColorPicker, center_slider_grabbers);
1695
1696 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, screen_picker);
1697 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, expanded_arrow);
1698 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, folded_arrow);
1699 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, add_preset);
1700
1701 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, shape_rect);
1702 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, shape_rect_wheel);
1703 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, shape_circle);
1704
1705 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, bar_arrow);
1706 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, sample_bg);
1707 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, overbright_indicator);
1708 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, picker_cursor);
1709 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, color_hue);
1710 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, color_okhsl_hue);
1711
1712 BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_normal, "tab_unselected", "TabContainer");
1713 BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_pressed, "tab_selected", "TabContainer");
1714 BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_hover, "tab_selected", "TabContainer");
1715}
1716
1717ColorPicker::ColorPicker() {
1718 internal_margin = memnew(MarginContainer);
1719 add_child(internal_margin, false, INTERNAL_MODE_FRONT);
1720
1721 VBoxContainer *real_vbox = memnew(VBoxContainer);
1722 internal_margin->add_child(real_vbox);
1723
1724 HBoxContainer *hb_edit = memnew(HBoxContainer);
1725 real_vbox->add_child(hb_edit);
1726 hb_edit->set_v_size_flags(SIZE_SHRINK_BEGIN);
1727
1728 uv_edit = memnew(Control);
1729 hb_edit->add_child(uv_edit);
1730 uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input).bind(uv_edit));
1731 uv_edit->set_mouse_filter(MOUSE_FILTER_PASS);
1732 uv_edit->set_h_size_flags(SIZE_EXPAND_FILL);
1733 uv_edit->set_v_size_flags(SIZE_EXPAND_FILL);
1734 uv_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, uv_edit));
1735
1736 sample_hbc = memnew(HBoxContainer);
1737 real_vbox->add_child(sample_hbc);
1738
1739 btn_pick = memnew(Button);
1740 sample_hbc->add_child(btn_pick);
1741 if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SCREEN_CAPTURE)) {
1742 btn_pick->set_tooltip_text(RTR("Pick a color from the screen."));
1743 btn_pick->connect(SNAME("pressed"), callable_mp(this, &ColorPicker::_pick_button_pressed));
1744 } else {
1745 // On unsupported platforms, use a legacy method for color picking.
1746 btn_pick->set_tooltip_text(RTR("Pick a color from the application window."));
1747 btn_pick->connect(SNAME("pressed"), callable_mp(this, &ColorPicker::_pick_button_pressed_legacy));
1748 }
1749
1750 sample = memnew(TextureRect);
1751 sample_hbc->add_child(sample);
1752 sample->set_h_size_flags(SIZE_EXPAND_FILL);
1753 sample->connect("gui_input", callable_mp(this, &ColorPicker::_sample_input));
1754 sample->connect("draw", callable_mp(this, &ColorPicker::_sample_draw));
1755
1756 btn_shape = memnew(MenuButton);
1757 btn_shape->set_flat(false);
1758 sample_hbc->add_child(btn_shape);
1759 btn_shape->set_toggle_mode(true);
1760 btn_shape->set_tooltip_text(RTR("Select a picker shape."));
1761
1762 current_shape = SHAPE_HSV_RECTANGLE;
1763
1764 shape_popup = btn_shape->get_popup();
1765 shape_popup->add_radio_check_item("HSV Rectangle", SHAPE_HSV_RECTANGLE);
1766 shape_popup->add_radio_check_item("HSV Wheel", SHAPE_HSV_WHEEL);
1767 shape_popup->add_radio_check_item("VHS Circle", SHAPE_VHS_CIRCLE);
1768 shape_popup->add_radio_check_item("OKHSL Circle", SHAPE_OKHSL_CIRCLE);
1769 shape_popup->set_item_checked(current_shape, true);
1770 shape_popup->connect("id_pressed", callable_mp(this, &ColorPicker::set_picker_shape));
1771
1772 btn_shape->set_icon(shape_popup->get_item_icon(current_shape));
1773
1774 add_mode(new ColorModeRGB(this));
1775 add_mode(new ColorModeHSV(this));
1776 add_mode(new ColorModeRAW(this));
1777 add_mode(new ColorModeOKHSL(this));
1778
1779 mode_hbc = memnew(HBoxContainer);
1780 real_vbox->add_child(mode_hbc);
1781
1782 mode_group.instantiate();
1783
1784 for (int i = 0; i < MODE_BUTTON_COUNT; i++) {
1785 mode_btns[i] = memnew(Button);
1786 mode_hbc->add_child(mode_btns[i]);
1787 mode_btns[i]->set_focus_mode(FOCUS_NONE);
1788 mode_btns[i]->set_h_size_flags(SIZE_EXPAND_FILL);
1789 mode_btns[i]->set_toggle_mode(true);
1790 mode_btns[i]->set_text(modes[i]->get_name());
1791 mode_btns[i]->set_button_group(mode_group);
1792 mode_btns[i]->connect("pressed", callable_mp(this, &ColorPicker::set_color_mode).bind((ColorModeType)i));
1793 }
1794 mode_btns[0]->set_pressed(true);
1795
1796 btn_mode = memnew(MenuButton);
1797 btn_mode->set_text("...");
1798 btn_mode->set_flat(false);
1799 mode_hbc->add_child(btn_mode);
1800 btn_mode->set_toggle_mode(true);
1801 btn_mode->set_tooltip_text(RTR("Select a picker mode."));
1802
1803 current_mode = MODE_RGB;
1804
1805 mode_popup = btn_mode->get_popup();
1806 for (int i = 0; i < modes.size(); i++) {
1807 mode_popup->add_radio_check_item(modes[i]->get_name(), i);
1808 }
1809 mode_popup->add_separator();
1810 mode_popup->add_check_item("Colorized Sliders", MODE_MAX);
1811 mode_popup->set_item_checked(current_mode, true);
1812 mode_popup->set_item_checked(MODE_MAX + 1, true);
1813 mode_popup->connect("id_pressed", callable_mp(this, &ColorPicker::_set_mode_popup_value));
1814 VBoxContainer *vbl = memnew(VBoxContainer);
1815 real_vbox->add_child(vbl);
1816
1817 VBoxContainer *vbr = memnew(VBoxContainer);
1818
1819 real_vbox->add_child(vbr);
1820 vbr->set_h_size_flags(SIZE_EXPAND_FILL);
1821
1822 slider_gc = memnew(GridContainer);
1823
1824 vbr->add_child(slider_gc);
1825 slider_gc->set_h_size_flags(SIZE_EXPAND_FILL);
1826 slider_gc->set_columns(3);
1827
1828 for (int i = 0; i < SLIDER_COUNT + 1; i++) {
1829 create_slider(slider_gc, i);
1830 }
1831
1832 alpha_label->set_text("A");
1833
1834 hex_hbc = memnew(HBoxContainer);
1835 hex_hbc->set_alignment(ALIGNMENT_BEGIN);
1836 vbr->add_child(hex_hbc);
1837
1838 hex_hbc->add_child(memnew(Label("Hex")));
1839
1840 text_type = memnew(Button);
1841 hex_hbc->add_child(text_type);
1842 text_type->set_text("#");
1843 text_type->set_tooltip_text(RTR("Switch between hexadecimal and code values."));
1844 if (Engine::get_singleton()->is_editor_hint()) {
1845 text_type->connect("pressed", callable_mp(this, &ColorPicker::_text_type_toggled));
1846 } else {
1847 text_type->set_flat(true);
1848 text_type->set_mouse_filter(MOUSE_FILTER_IGNORE);
1849 }
1850
1851 c_text = memnew(LineEdit);
1852 hex_hbc->add_child(c_text);
1853 c_text->set_h_size_flags(SIZE_EXPAND_FILL);
1854 c_text->set_select_all_on_focus(true);
1855 c_text->set_tooltip_text(RTR("Enter a hex code (\"#ff0000\") or named color (\"red\")."));
1856 c_text->set_placeholder(RTR("Hex code or named color"));
1857 c_text->connect("text_submitted", callable_mp(this, &ColorPicker::_html_submitted));
1858 c_text->connect("text_changed", callable_mp(this, &ColorPicker::_text_changed));
1859 c_text->connect("focus_exited", callable_mp(this, &ColorPicker::_html_focus_exit));
1860
1861 wheel_edit = memnew(AspectRatioContainer);
1862 wheel_edit->set_h_size_flags(SIZE_EXPAND_FILL);
1863 wheel_edit->set_v_size_flags(SIZE_EXPAND_FILL);
1864 hb_edit->add_child(wheel_edit);
1865
1866 wheel_mat.instantiate();
1867 wheel_mat->set_shader(wheel_shader);
1868 circle_mat.instantiate();
1869 circle_mat->set_shader(circle_shader);
1870
1871 wheel_margin = memnew(MarginContainer);
1872 wheel_margin->add_theme_constant_override("margin_bottom", 8);
1873 wheel_edit->add_child(wheel_margin);
1874
1875 wheel = memnew(Control);
1876 wheel_margin->add_child(wheel);
1877 wheel->set_mouse_filter(MOUSE_FILTER_PASS);
1878 wheel->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(2, wheel));
1879
1880 wheel_uv = memnew(Control);
1881 wheel_margin->add_child(wheel_uv);
1882 wheel_uv->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input).bind(wheel_uv));
1883 wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(0, wheel_uv));
1884
1885 w_edit = memnew(Control);
1886 hb_edit->add_child(w_edit);
1887 w_edit->set_h_size_flags(SIZE_FILL);
1888 w_edit->set_v_size_flags(SIZE_EXPAND_FILL);
1889 w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input));
1890 w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw).bind(1, w_edit));
1891
1892 _update_controls();
1893 updating = false;
1894
1895 preset_container = memnew(GridContainer);
1896 preset_container->set_h_size_flags(SIZE_EXPAND_FILL);
1897 preset_container->set_columns(PRESET_COLUMN_COUNT);
1898 preset_container->hide();
1899
1900 preset_group.instantiate();
1901
1902 btn_preset = memnew(Button);
1903 btn_preset->set_text("Swatches");
1904 btn_preset->set_flat(true);
1905 btn_preset->set_toggle_mode(true);
1906 btn_preset->set_focus_mode(FOCUS_NONE);
1907 btn_preset->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
1908 btn_preset->connect("toggled", callable_mp(this, &ColorPicker::_show_hide_preset).bind(btn_preset, preset_container));
1909 real_vbox->add_child(btn_preset);
1910
1911 real_vbox->add_child(preset_container);
1912
1913 recent_preset_hbc = memnew(HBoxContainer);
1914 recent_preset_hbc->set_v_size_flags(SIZE_SHRINK_BEGIN);
1915 recent_preset_hbc->hide();
1916
1917 recent_preset_group.instantiate();
1918
1919 btn_recent_preset = memnew(Button);
1920 btn_recent_preset->set_text("Recent Colors");
1921 btn_recent_preset->set_flat(true);
1922 btn_recent_preset->set_toggle_mode(true);
1923 btn_recent_preset->set_focus_mode(FOCUS_NONE);
1924 btn_recent_preset->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
1925 btn_recent_preset->connect("toggled", callable_mp(this, &ColorPicker::_show_hide_preset).bind(btn_recent_preset, recent_preset_hbc));
1926 real_vbox->add_child(btn_recent_preset);
1927
1928 real_vbox->add_child(recent_preset_hbc);
1929
1930 set_pick_color(Color(1, 1, 1));
1931
1932 btn_add_preset = memnew(Button);
1933 btn_add_preset->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
1934 btn_add_preset->set_tooltip_text(RTR("Add current color as a preset."));
1935 btn_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed));
1936 preset_container->add_child(btn_add_preset);
1937}
1938
1939ColorPicker::~ColorPicker() {
1940 for (int i = 0; i < modes.size(); i++) {
1941 delete modes[i];
1942 }
1943}
1944
1945/////////////////
1946
1947void ColorPickerButton::_about_to_popup() {
1948 set_pressed(true);
1949 if (picker) {
1950 picker->set_old_color(color);
1951 }
1952}
1953
1954void ColorPickerButton::_color_changed(const Color &p_color) {
1955 color = p_color;
1956 queue_redraw();
1957 emit_signal(SNAME("color_changed"), color);
1958}
1959
1960void ColorPickerButton::_modal_closed() {
1961 emit_signal(SNAME("popup_closed"));
1962 set_pressed(false);
1963}
1964
1965void ColorPickerButton::pressed() {
1966 _update_picker();
1967
1968 Size2 minsize = popup->get_contents_minimum_size();
1969 float viewport_height = get_viewport_rect().size.y;
1970
1971 popup->reset_size();
1972 picker->_update_presets();
1973 picker->_update_recent_presets();
1974
1975 // Determine in which direction to show the popup. By default popup horizontally centered below the button.
1976 // But if the popup doesn't fit below and the button is in the bottom half of the viewport, show above.
1977 bool show_above = false;
1978 if (get_global_position().y + get_size().y + minsize.y > viewport_height && get_global_position().y * 2 + get_size().y > viewport_height) {
1979 show_above = true;
1980 }
1981
1982 float h_offset = (get_size().x - minsize.x) / 2;
1983 float v_offset = show_above ? -minsize.y : get_size().y;
1984 popup->set_position(get_screen_position() + Vector2(h_offset, v_offset));
1985 popup->popup();
1986 picker->set_focus_on_line_edit();
1987}
1988
1989void ColorPickerButton::_notification(int p_what) {
1990 switch (p_what) {
1991 case NOTIFICATION_DRAW: {
1992 const Rect2 r = Rect2(theme_cache.normal_style->get_offset(), get_size() - theme_cache.normal_style->get_minimum_size());
1993 draw_texture_rect(theme_cache.background_icon, r, true);
1994 draw_rect(r, color);
1995
1996 if (color.r > 1 || color.g > 1 || color.b > 1) {
1997 // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview
1998 draw_texture(theme_cache.overbright_indicator, theme_cache.normal_style->get_offset());
1999 }
2000 } break;
2001
2002 case NOTIFICATION_WM_CLOSE_REQUEST: {
2003 if (popup) {
2004 popup->hide();
2005 }
2006 } break;
2007
2008 case NOTIFICATION_VISIBILITY_CHANGED: {
2009 if (popup && !is_visible_in_tree()) {
2010 popup->hide();
2011 }
2012 } break;
2013 }
2014}
2015
2016void ColorPickerButton::set_pick_color(const Color &p_color) {
2017 if (color == p_color) {
2018 return;
2019 }
2020 color = p_color;
2021 if (picker) {
2022 picker->set_pick_color(p_color);
2023 }
2024
2025 queue_redraw();
2026}
2027
2028Color ColorPickerButton::get_pick_color() const {
2029 return color;
2030}
2031
2032void ColorPickerButton::set_edit_alpha(bool p_show) {
2033 if (edit_alpha == p_show) {
2034 return;
2035 }
2036 edit_alpha = p_show;
2037 if (picker) {
2038 picker->set_edit_alpha(p_show);
2039 }
2040}
2041
2042bool ColorPickerButton::is_editing_alpha() const {
2043 return edit_alpha;
2044}
2045
2046ColorPicker *ColorPickerButton::get_picker() {
2047 _update_picker();
2048 return picker;
2049}
2050
2051PopupPanel *ColorPickerButton::get_popup() {
2052 _update_picker();
2053 return popup;
2054}
2055
2056void ColorPickerButton::_update_picker() {
2057 if (!picker) {
2058 popup = memnew(PopupPanel);
2059 popup->set_wrap_controls(true);
2060 picker = memnew(ColorPicker);
2061 picker->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
2062 popup->add_child(picker);
2063 add_child(popup, false, INTERNAL_MODE_FRONT);
2064 picker->connect("color_changed", callable_mp(this, &ColorPickerButton::_color_changed));
2065 popup->connect("about_to_popup", callable_mp(this, &ColorPickerButton::_about_to_popup));
2066 popup->connect("popup_hide", callable_mp(this, &ColorPickerButton::_modal_closed));
2067 picker->connect("minimum_size_changed", callable_mp((Window *)popup, &Window::reset_size));
2068 picker->set_pick_color(color);
2069 picker->set_edit_alpha(edit_alpha);
2070 picker->set_display_old_color(true);
2071 emit_signal(SNAME("picker_created"));
2072 }
2073}
2074
2075void ColorPickerButton::_bind_methods() {
2076 ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPickerButton::set_pick_color);
2077 ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPickerButton::get_pick_color);
2078 ClassDB::bind_method(D_METHOD("get_picker"), &ColorPickerButton::get_picker);
2079 ClassDB::bind_method(D_METHOD("get_popup"), &ColorPickerButton::get_popup);
2080 ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPickerButton::set_edit_alpha);
2081 ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPickerButton::is_editing_alpha);
2082 ClassDB::bind_method(D_METHOD("_about_to_popup"), &ColorPickerButton::_about_to_popup);
2083
2084 ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
2085 ADD_SIGNAL(MethodInfo("popup_closed"));
2086 ADD_SIGNAL(MethodInfo("picker_created"));
2087 ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color");
2088 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
2089
2090 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ColorPickerButton, normal_style, "normal");
2091 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, ColorPickerButton, background_icon, "bg");
2092 BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, ColorPickerButton, overbright_indicator, "overbright_indicator", "ColorPicker");
2093}
2094
2095ColorPickerButton::ColorPickerButton(const String &p_text) :
2096 Button(p_text) {
2097 set_toggle_mode(true);
2098}
2099
2100/////////////////
2101
2102void ColorPresetButton::_notification(int p_what) {
2103 switch (p_what) {
2104 case NOTIFICATION_DRAW: {
2105 const Rect2 r = Rect2(Point2(0, 0), get_size());
2106 Ref<StyleBox> sb_raw = theme_cache.foreground_style->duplicate();
2107 Ref<StyleBoxFlat> sb_flat = sb_raw;
2108 Ref<StyleBoxTexture> sb_texture = sb_raw;
2109
2110 if (sb_flat.is_valid()) {
2111 sb_flat->set_border_width(SIDE_BOTTOM, 2);
2112 if (get_draw_mode() == DRAW_PRESSED || get_draw_mode() == DRAW_HOVER_PRESSED) {
2113 sb_flat->set_border_color(Color(1, 1, 1, 1));
2114 } else {
2115 sb_flat->set_border_color(Color(0, 0, 0, 1));
2116 }
2117
2118 if (preset_color.a < 1) {
2119 // Draw a background pattern when the color is transparent.
2120 sb_flat->set_bg_color(Color(1, 1, 1));
2121 sb_flat->draw(get_canvas_item(), r);
2122
2123 Rect2 bg_texture_rect = r.grow_side(SIDE_LEFT, -sb_flat->get_margin(SIDE_LEFT));
2124 bg_texture_rect = bg_texture_rect.grow_side(SIDE_RIGHT, -sb_flat->get_margin(SIDE_RIGHT));
2125 bg_texture_rect = bg_texture_rect.grow_side(SIDE_TOP, -sb_flat->get_margin(SIDE_TOP));
2126 bg_texture_rect = bg_texture_rect.grow_side(SIDE_BOTTOM, -sb_flat->get_margin(SIDE_BOTTOM));
2127
2128 draw_texture_rect(theme_cache.background_icon, bg_texture_rect, true);
2129 sb_flat->set_bg_color(preset_color);
2130 }
2131 sb_flat->set_bg_color(preset_color);
2132 sb_flat->draw(get_canvas_item(), r);
2133 } else if (sb_texture.is_valid()) {
2134 if (preset_color.a < 1) {
2135 // Draw a background pattern when the color is transparent.
2136 bool use_tile_texture = (sb_texture->get_h_axis_stretch_mode() == StyleBoxTexture::AxisStretchMode::AXIS_STRETCH_MODE_TILE) || (sb_texture->get_h_axis_stretch_mode() == StyleBoxTexture::AxisStretchMode::AXIS_STRETCH_MODE_TILE_FIT);
2137 draw_texture_rect(theme_cache.background_icon, r, use_tile_texture);
2138 }
2139 sb_texture->set_modulate(preset_color);
2140 sb_texture->draw(get_canvas_item(), r);
2141 } else {
2142 WARN_PRINT("Unsupported StyleBox used for ColorPresetButton. Use StyleBoxFlat or StyleBoxTexture instead.");
2143 }
2144 if (preset_color.r > 1 || preset_color.g > 1 || preset_color.b > 1) {
2145 // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview
2146 draw_texture(theme_cache.overbright_indicator, Vector2(0, 0));
2147 }
2148
2149 } break;
2150 }
2151}
2152
2153void ColorPresetButton::set_preset_color(const Color &p_color) {
2154 preset_color = p_color;
2155}
2156
2157Color ColorPresetButton::get_preset_color() const {
2158 return preset_color;
2159}
2160
2161void ColorPresetButton::_bind_methods() {
2162 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ColorPresetButton, foreground_style, "preset_fg");
2163 BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, ColorPresetButton, background_icon, "preset_bg");
2164 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPresetButton, overbright_indicator);
2165}
2166
2167ColorPresetButton::ColorPresetButton(Color p_color, int p_size) {
2168 preset_color = p_color;
2169 set_toggle_mode(true);
2170 set_custom_minimum_size(Size2(p_size, p_size));
2171}
2172
2173ColorPresetButton::~ColorPresetButton() {
2174}
2175