diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml
index ce046e44a6a..72db9d8081d 100644
--- a/doc/classes/ColorPicker.xml
+++ b/doc/classes/ColorPicker.xml
@@ -73,6 +73,9 @@
If [code]true[/code], shows an alpha channel slider (opacity).
+
+ If [code]true[/code], shows an intensity slider. The intensity is applied as follows: multiply the color by [code]2 ** intensity[/code] in linear RGB space, and then convert it back to sRGB.
+
If [code]true[/code], the hex color code input field is visible.
@@ -111,13 +114,15 @@
- Allows editing the color with Red/Green/Blue sliders.
+ Allows editing the color with Red/Green/Blue sliders in sRGB color space.
Allows editing the color with Hue/Saturation/Value sliders.
-
- Allows the color R, G, B component values to go beyond 1.0, which can be used for certain special operations that require it (like tinting without darkening or rendering sprites in HDR).
+
+
+
+ Allows editing the color with Red/Green/Blue sliders in linear color space.
Allows editing the color with Hue/Saturation/Lightness sliders.
@@ -171,6 +176,9 @@
Custom texture for the hue selection slider on the right.
+
+ The icon for the button that switches color text to hexadecimal.
+
The icon for color preset drop down menu when expanded.
diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml
index bec25203971..0852929fe67 100644
--- a/doc/classes/ColorPickerButton.xml
+++ b/doc/classes/ColorPickerButton.xml
@@ -35,6 +35,9 @@
If [code]true[/code], the alpha channel in the displayed [ColorPicker] will be visible.
+
+ If [code]true[/code], the intensity slider in the displayed [ColorPicker] will be visible.
+
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index a98c6e96607..77f63f995ac 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -973,6 +973,9 @@
If [code]true[/code], automatically expands property groups in the Inspector dock when opening a scene that hasn't been opened previously. If [code]false[/code], all groups remain collapsed by default.
+
+ If [code]true[/code], show the intensity slider in the [ColorPicker]s opened in the editor.
+
The default color picker mode to use when opening [ColorPicker]s in the editor. This mode can be temporarily adjusted on the color picker itself.
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 18a3645bab0..330bc9d50f5 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -4196,9 +4196,11 @@ void EditorNode::setup_color_picker(ColorPicker *p_picker) {
p_picker->set_editor_settings(EditorSettings::get_singleton());
int default_color_mode = EditorSettings::get_singleton()->get_project_metadata("color_picker", "color_mode", EDITOR_GET("interface/inspector/default_color_picker_mode"));
int picker_shape = EditorSettings::get_singleton()->get_project_metadata("color_picker", "picker_shape", EDITOR_GET("interface/inspector/default_color_picker_shape"));
+ bool show_intensity = EditorSettings::get_singleton()->get_project_metadata("color_picker", "show_intensity", EDITOR_GET("interface/inspector/color_picker_show_intensity"));
p_picker->set_color_mode((ColorPicker::ColorModeType)default_color_mode);
p_picker->set_picker_shape((ColorPicker::PickerShapeType)picker_shape);
+ p_picker->set_edit_intensity(show_intensity);
p_picker->set_quick_open_callback(callable_mp(this, &EditorNode::_palette_quick_open_dialog));
p_picker->set_palette_saved_callback(callable_mp(EditorFileSystem::get_singleton(), &EditorFileSystem::update_file));
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index f46451878dd..332e866bc10 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -570,6 +570,7 @@ void EditorSettings::_load_defaults(Ref p_extra_config) {
EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "interface/inspector/default_color_picker_mode", (int32_t)ColorPicker::MODE_RGB, "RGB,HSV,RAW,OKHSL")
EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "interface/inspector/default_color_picker_shape", (int32_t)ColorPicker::SHAPE_OKHSL_CIRCLE, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle,OKHSL Circle")
+ EDITOR_SETTING_BASIC(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/color_picker_show_intensity", true, "");
// Theme
EDITOR_SETTING_BASIC(Variant::BOOL, PROPERTY_HINT_ENUM, "interface/theme/follow_system_theme", false, "")
diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp
index 3b824ea765c..966d0a4ce2d 100644
--- a/editor/themes/editor_theme_manager.cpp
+++ b/editor/themes/editor_theme_manager.cpp
@@ -1821,6 +1821,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref &p_the
p_theme->set_icon("bar_arrow", "ColorPicker", p_theme->get_icon(SNAME("ColorPickerBarArrow"), EditorStringName(EditorIcons)));
p_theme->set_icon("picker_cursor", "ColorPicker", p_theme->get_icon(SNAME("PickerCursor"), EditorStringName(EditorIcons)));
p_theme->set_icon("picker_cursor_bg", "ColorPicker", p_theme->get_icon(SNAME("PickerCursorBg"), EditorStringName(EditorIcons)));
+ p_theme->set_icon("color_script", "ColorPicker", p_theme->get_icon(SNAME("Script"), EditorStringName(EditorIcons)));
// ColorPickerButton.
p_theme->set_icon("bg", "ColorPickerButton", p_theme->get_icon(SNAME("GuiMiniCheckerboard"), EditorStringName(EditorIcons)));
diff --git a/scene/gui/color_mode.cpp b/scene/gui/color_mode.cpp
index 119cb72693b..81992c5c143 100644
--- a/scene/gui/color_mode.cpp
+++ b/scene/gui/color_mode.cpp
@@ -39,13 +39,13 @@ ColorMode::ColorMode(ColorPicker *p_color_picker) {
}
String ColorModeRGB::get_slider_label(int idx) const {
- ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label.");
+ ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), String(), "Couldn't get slider label.");
return labels[idx];
}
float ColorModeRGB::get_slider_value(int idx) const {
- ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider value.");
- return color_picker->get_pick_color().components[idx] * 255;
+ ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), 0, "Couldn't get slider value.");
+ return color_picker->color_normalized.components[idx] * 255;
}
Color ColorModeRGB::get_color() const {
@@ -57,6 +57,25 @@ Color ColorModeRGB::get_color() const {
return color;
}
+void ColorModeRGB::_greater_value_inputted() {
+ HSlider **sliders = color_picker->sliders;
+ Color color_prev = color_picker->color;
+ for (int i = 0; i < 3; i++) {
+ if (sliders[i]->get_value() > 255) {
+ color_prev.components[i] = sliders[i]->get_value() / 255.0;
+ }
+ }
+ Color linear_color = color_prev.srgb_to_linear();
+ float multiplier = MAX(1, MAX(MAX(linear_color.r, linear_color.g), linear_color.b));
+ Color srgb = Color(linear_color.r / multiplier, linear_color.g / multiplier, linear_color.b / multiplier, linear_color.a).linear_to_srgb();
+ sliders[0]->set_value_no_signal(srgb.r * 255);
+ sliders[1]->set_value_no_signal(srgb.g * 255);
+ sliders[2]->set_value_no_signal(srgb.b * 255);
+
+ color_picker->intensity = Math::log2(multiplier);
+ color_picker->intensity_slider->set_value_no_signal(color_picker->intensity);
+}
+
void ColorModeRGB::slider_draw(int p_which) {
Vector pos;
pos.resize(4);
@@ -66,37 +85,39 @@ void ColorModeRGB::slider_draw(int p_which) {
Size2 size = slider->get_size();
Color left_color;
Color right_color;
- Color color = color_picker->get_pick_color();
+ Color color = color_picker->color_normalized;
const real_t margin = 16 * color_picker->theme_cache.base_scale;
- if (p_which == ColorPicker::SLIDER_COUNT) {
- slider->draw_texture_rect(color_picker->theme_cache.sample_bg, Rect2(Point2(0, 0), Size2(size.x, margin)), true);
+ left_color = Color(
+ p_which == 0 ? 0 : color.r,
+ p_which == 1 ? 0 : color.g,
+ p_which == 2 ? 0 : color.b);
+ right_color = Color(
+ p_which == 0 ? 1 : color.r,
+ p_which == 1 ? 1 : color.g,
+ p_which == 2 ? 1 : color.b);
- left_color = color;
- left_color.a = 0;
- right_color = color;
- right_color.a = 1;
- } else {
- left_color = Color(
- p_which == 0 ? 0 : color.r,
- p_which == 1 ? 0 : color.g,
- p_which == 2 ? 0 : color.b);
- right_color = Color(
- p_which == 0 ? 1 : color.r,
- p_which == 1 ? 1 : color.g,
- p_which == 2 ? 1 : color.b);
+ if (rgb_texture[p_which].is_null()) {
+ rgb_texture[p_which].instantiate();
+ rgb_texture[p_which]->set_width(400);
+ rgb_texture[p_which]->set_height(6);
}
- col.set(0, left_color);
- col.set(1, right_color);
- col.set(2, right_color);
- col.set(3, left_color);
- pos.set(0, Vector2(0, 0));
- pos.set(1, Vector2(size.x, 0));
- pos.set(2, Vector2(size.x, margin));
- pos.set(3, Vector2(0, margin));
+ Ref gradient_texture = rgb_texture[p_which];
- slider->draw_polygon(pos, col);
+ Ref gradient = gradient_texture->get_gradient();
+ if (gradient.is_null()) {
+ gradient.instantiate();
+ PackedFloat32Array offsets = { 0, 1 };
+ gradient->set_offsets(offsets);
+ gradient->set_interpolation_color_space(Gradient::ColorSpace::GRADIENT_COLOR_SPACE_SRGB);
+ gradient_texture->set_gradient(gradient);
+ }
+
+ PackedColorArray colors = { left_color, right_color };
+ gradient->set_colors(colors);
+
+ slider->draw_texture_rect(gradient_texture, Rect2(Vector2(), Vector2(size.x, margin)), false);
}
void ColorModeHSV::_value_changed() {
@@ -108,38 +129,43 @@ void ColorModeHSV::_value_changed() {
if (values[2] > 0 || values[1] != cached_saturation) {
cached_saturation = values[1];
}
+
+ // Cache real HSV values in ColorPicker.
+ color_picker->h = color_picker->sliders[0]->get_value() / 360.0;
+ color_picker->s = color_picker->sliders[1]->get_value() / 100.0;
+ color_picker->v = color_picker->sliders[2]->get_value() / 100.0;
+
+ color_picker->hsv_cached = true;
}
String ColorModeHSV::get_slider_label(int idx) const {
- ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label.");
+ ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), String(), "Couldn't get slider label.");
return labels[idx];
}
float ColorModeHSV::get_slider_max(int idx) const {
- ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value.");
+ ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), 0, "Couldn't get slider max value.");
return slider_max[idx];
}
float ColorModeHSV::get_slider_value(int idx) const {
switch (idx) {
case 0: {
- if (color_picker->get_pick_color().get_s() > 0) {
- return color_picker->get_pick_color().get_h() * 360.0;
+ if (color_picker->color_normalized.get_s() > 0) {
+ return color_picker->color_normalized.get_h() * 360.0;
} else {
return cached_hue;
}
}
case 1: {
- if (color_picker->get_pick_color().get_v() > 0) {
- return color_picker->get_pick_color().get_s() * 100.0;
+ if (color_picker->color_normalized.get_v() > 0) {
+ return color_picker->color_normalized.get_s() * 100.0;
} else {
return cached_saturation;
}
}
case 2:
- return color_picker->get_pick_color().get_v() * 100.0;
- case 3:
- return Math::round(color_picker->get_pick_color().components[3] * 255.0);
+ return color_picker->color_normalized.get_v() * 100.0;
default:
ERR_FAIL_V_MSG(0, "Couldn't get slider value.");
}
@@ -147,8 +173,7 @@ float ColorModeHSV::get_slider_value(int idx) const {
Color ColorModeHSV::get_color() const {
Vector values = color_picker->get_active_slider_values();
- Color color;
- color.set_hsv(values[0] / 360.0, values[1] / 100.0, values[2] / 100.0, values[3] / 255.0);
+ Color color = Color::from_hsv(values[0] / 360.0, values[1] / 100.0, values[2] / 100.0, values[3] / 255.0);
return color;
}
@@ -161,17 +186,10 @@ void ColorModeHSV::slider_draw(int p_which) {
Size2 size = slider->get_size();
Color left_color;
Color right_color;
- Color color = color_picker->get_pick_color();
+ Color color = color_picker->color_normalized;
const real_t margin = 16 * color_picker->theme_cache.base_scale;
- if (p_which == ColorPicker::SLIDER_COUNT) {
- slider->draw_texture_rect(color_picker->theme_cache.sample_bg, Rect2(Point2(0, 0), Size2(size.x, margin)), true);
-
- left_color = color;
- left_color.a = 0;
- right_color = color;
- right_color.a = 1;
- } else if (p_which == 0) {
+ if (p_which == 0) {
float v = color.get_v();
left_color = Color(v, v, v);
right_color = left_color;
@@ -203,31 +221,60 @@ void ColorModeHSV::slider_draw(int p_which) {
}
}
-String ColorModeRAW::get_slider_label(int idx) const {
- ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label.");
+String ColorModeLinear::get_slider_label(int idx) const {
+ ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), String(), "Couldn't get slider label.");
return labels[idx];
}
-float ColorModeRAW::get_slider_max(int idx) const {
- ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value.");
+float ColorModeLinear::get_slider_max(int idx) const {
+ ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), 0, "Couldn't get slider max value.");
return slider_max[idx];
}
-float ColorModeRAW::get_slider_value(int idx) const {
- ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider value.");
- return color_picker->get_pick_color().components[idx];
+float ColorModeLinear::get_slider_value(int idx) const {
+ ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), 0, "Couldn't get slider value.");
+ Color color = color_picker->color_normalized.srgb_to_linear();
+ return color.components[idx];
}
-Color ColorModeRAW::get_color() const {
+float ColorModeLinear::get_alpha_slider_max() const {
+ return 1;
+}
+
+float ColorModeLinear::get_alpha_slider_value() const {
+ return color_picker->get_pick_color().a;
+}
+
+Color ColorModeLinear::get_color() const {
Vector values = color_picker->get_active_slider_values();
Color color;
for (int i = 0; i < 4; i++) {
color.components[i] = values[i];
}
- return color;
+ return color.linear_to_srgb();
}
-void ColorModeRAW::slider_draw(int p_which) {
+void ColorModeLinear::_greater_value_inputted() {
+ HSlider **sliders = color_picker->sliders;
+ Color color_prev = color_picker->color;
+ Color linear_color = color_prev.srgb_to_linear();
+ for (int i = 0; i < 3; i++) {
+ if (sliders[i]->get_value() > 1 + CMP_EPSILON) {
+ linear_color.components[i] = sliders[i]->get_value();
+ }
+ }
+
+ float multiplier = MAX(1, MAX(MAX(linear_color.r, linear_color.g), linear_color.b));
+
+ sliders[0]->set_value_no_signal(linear_color.r / multiplier);
+ sliders[1]->set_value_no_signal(linear_color.g / multiplier);
+ sliders[2]->set_value_no_signal(linear_color.b / multiplier);
+
+ color_picker->intensity = Math::log2(multiplier);
+ color_picker->intensity_slider->set_value_no_signal(color_picker->intensity);
+}
+
+void ColorModeLinear::slider_draw(int p_which) {
Vector pos;
pos.resize(4);
Vector col;
@@ -236,40 +283,39 @@ void ColorModeRAW::slider_draw(int p_which) {
Size2 size = slider->get_size();
Color left_color;
Color right_color;
- Color color = color_picker->get_pick_color();
+ Color color = color_picker->color_normalized.linear_to_srgb();
const real_t margin = 16 * color_picker->theme_cache.base_scale;
- if (p_which == ColorPicker::SLIDER_COUNT) {
- slider->draw_texture_rect(color_picker->theme_cache.sample_bg, Rect2(Point2(0, 0), Size2(size.x, margin)), true);
+ left_color = Color(
+ p_which == 0 ? 0 : color.r,
+ p_which == 1 ? 0 : color.g,
+ p_which == 2 ? 0 : color.b);
+ right_color = Color(
+ p_which == 0 ? 1 : color.r,
+ p_which == 1 ? 1 : color.g,
+ p_which == 2 ? 1 : color.b);
- left_color = color;
- left_color.a = 0;
- right_color = color;
- right_color.a = 1;
-
- col.set(0, left_color);
- col.set(1, right_color);
- col.set(2, right_color);
- col.set(3, left_color);
- pos.set(0, Vector2(0, 0));
- pos.set(1, Vector2(size.x, 0));
- pos.set(2, Vector2(size.x, margin));
- pos.set(3, Vector2(0, margin));
-
- slider->draw_polygon(pos, col);
- }
-}
-
-bool ColorModeRAW::apply_theme() const {
- for (int i = 0; i < ColorPicker::SLIDER_COUNT; i++) {
- HSlider *slider = color_picker->get_slider(i);
- slider->remove_theme_icon_override("grabber");
- slider->remove_theme_icon_override("grabber_highlight");
- slider->remove_theme_style_override("slider");
- slider->remove_theme_constant_override("grabber_offset");
+ if (rgb_texture[p_which].is_null()) {
+ rgb_texture[p_which].instantiate();
+ rgb_texture[p_which]->set_width(400);
+ rgb_texture[p_which]->set_height(6);
}
- return true;
+ Ref gradient_texture = rgb_texture[p_which];
+
+ Ref gradient = gradient_texture->get_gradient();
+ if (gradient.is_null()) {
+ gradient.instantiate();
+ PackedFloat32Array offsets = { 0, 1 };
+ gradient->set_offsets(offsets);
+ gradient->set_interpolation_color_space(Gradient::ColorSpace::GRADIENT_COLOR_SPACE_LINEAR_SRGB);
+ gradient_texture->set_gradient(gradient);
+ }
+
+ PackedColorArray colors = { left_color, right_color };
+ gradient->set_colors(colors);
+
+ slider->draw_texture_rect(gradient_texture, Rect2(Vector2(), Vector2(size.x, margin)), false);
}
void ColorModeOKHSL::_value_changed() {
@@ -281,38 +327,43 @@ void ColorModeOKHSL::_value_changed() {
if (values[2] > 0 || values[1] != cached_saturation) {
cached_saturation = values[1];
}
+
+ // Cache real OKHSL values in ColorPicker.
+ color_picker->ok_hsl_h = color_picker->sliders[0]->get_value() / 360.0;
+ color_picker->ok_hsl_s = color_picker->sliders[1]->get_value() / 100.0;
+ color_picker->ok_hsl_l = color_picker->sliders[2]->get_value() / 100.0;
+
+ color_picker->okhsl_cached = true;
}
String ColorModeOKHSL::get_slider_label(int idx) const {
- ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label.");
+ ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), String(), "Couldn't get slider label.");
return labels[idx];
}
float ColorModeOKHSL::get_slider_max(int idx) const {
- ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value.");
+ ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), 0, "Couldn't get slider max value.");
return slider_max[idx];
}
float ColorModeOKHSL::get_slider_value(int idx) const {
switch (idx) {
case 0: {
- if (color_picker->get_pick_color().get_ok_hsl_s() > 0) {
- return color_picker->get_pick_color().get_ok_hsl_h() * 360.0;
+ if (color_picker->color_normalized.get_ok_hsl_s() > 0) {
+ return color_picker->color_normalized.get_ok_hsl_h() * 360.0;
} else {
return cached_hue;
}
}
case 1: {
- if (color_picker->get_pick_color().get_ok_hsl_l() > 0) {
- return color_picker->get_pick_color().get_ok_hsl_s() * 100.0;
+ if (color_picker->color_normalized.get_ok_hsl_l() > 0) {
+ return color_picker->color_normalized.get_ok_hsl_s() * 100.0;
} else {
return cached_saturation;
}
}
case 2:
- return color_picker->get_pick_color().get_ok_hsl_l() * 100.0;
- case 3:
- return Math::round(color_picker->get_pick_color().components[3] * 255.0);
+ return color_picker->color_normalized.get_ok_hsl_l() * 100.0;
default:
ERR_FAIL_V_MSG(0, "Couldn't get slider value.");
}
@@ -320,8 +371,7 @@ float ColorModeOKHSL::get_slider_value(int idx) const {
Color ColorModeOKHSL::get_color() const {
Vector values = color_picker->get_active_slider_values();
- Color color;
- color.set_ok_hsl(values[0] / 360.0, values[1] / 100.0, values[2] / 100.0, values[3] / 255.0);
+ Color color = Color::from_ok_hsl(values[0] / 360.0, values[1] / 100.0, values[2] / 100.0, values[3] / 255.0);
return color;
}
@@ -334,17 +384,16 @@ void ColorModeOKHSL::slider_draw(int p_which) {
Vector col;
Color left_color;
Color right_color;
- Color color = color_picker->get_pick_color();
+ Color color = color_picker->color_normalized;
+ float okhsl_l = color.get_ok_hsl_l();
+ float slider_hue = (Math::is_zero_approx(color.get_ok_hsl_s())) ? cached_hue / 360.0 : color.get_ok_hsl_h();
+ float slider_sat = (Math::is_zero_approx(okhsl_l) || Math::is_equal_approx(okhsl_l, 1)) ? cached_saturation / 100.0 : color.get_ok_hsl_s();
if (p_which == 2) { // L
pos.resize(6);
col.resize(6);
left_color = Color(0, 0, 0);
- Color middle_color;
- float slider_hue = (Math::is_zero_approx(color.get_ok_hsl_s())) ? cached_hue / 360.0 : color.get_ok_hsl_h();
- float slider_sat = (Math::is_zero_approx(color.get_ok_hsl_l())) ? cached_saturation / 100.0 : color.get_ok_hsl_s();
-
- middle_color.set_ok_hsl(slider_hue, slider_sat, 0.5);
+ Color middle_color = Color::from_ok_hsl(slider_hue, slider_sat, 0.5);
right_color.set_ok_hsl(slider_hue, slider_sat, 1);
col.set(0, left_color);
@@ -359,26 +408,13 @@ void ColorModeOKHSL::slider_draw(int p_which) {
pos.set(3, Vector2(size.x, margin));
pos.set(4, Vector2(size.x * 0.5, margin));
pos.set(5, Vector2(0, margin));
- } else {
+ slider->draw_polygon(pos, col);
+ } else if (p_which == 1) { // S
pos.resize(4);
col.resize(4);
- if (p_which == ColorPicker::SLIDER_COUNT) {
- slider->draw_texture_rect(color_picker->theme_cache.sample_bg, Rect2(Point2(0, 0), Size2(size.x, margin)), true);
-
- left_color = color;
- left_color.a = 0;
- right_color = color;
- right_color.a = 1;
- } else if (p_which == 0) {
- float l = color.get_ok_hsl_l();
- left_color = Color(l, l, l);
- right_color = left_color;
- } else {
- left_color.set_ok_hsl(color.get_ok_hsl_h(), 0, color.get_ok_hsl_l());
- float s_col_hue = (Math::is_zero_approx(color.get_ok_hsl_s())) ? cached_hue / 360.0 : color.get_ok_hsl_h();
- right_color.set_ok_hsl(s_col_hue, 1, color.get_ok_hsl_l());
- }
+ left_color.set_ok_hsl(slider_hue, 0, okhsl_l);
+ right_color.set_ok_hsl(slider_hue, 1, okhsl_l);
col.set(0, left_color);
col.set(1, right_color);
@@ -388,34 +424,36 @@ void ColorModeOKHSL::slider_draw(int p_which) {
pos.set(1, Vector2(size.x, 0));
pos.set(2, Vector2(size.x, margin));
pos.set(3, Vector2(0, margin));
- }
-
- slider->draw_polygon(pos, col);
-
- if (p_which == 0) { // H
+ slider->draw_polygon(pos, col);
+ } else if (p_which == 0) { // H
const int precision = 7;
+ if (hue_texture.is_null()) {
+ hue_texture.instantiate();
+ hue_texture->set_width(400);
+ hue_texture->set_height(6);
+ }
+ Ref hue_gradient = hue_texture->get_gradient();
+ if (hue_gradient.is_null()) {
+ hue_gradient.instantiate();
+ PackedFloat32Array offsets;
+ offsets.resize(precision);
+ for (int i = 0; i < precision; i++) {
+ float h = i / float(precision - 1);
+ offsets.write[i] = h;
+ }
+ hue_gradient->set_offsets(offsets);
+ hue_gradient->set_interpolation_color_space(Gradient::ColorSpace::GRADIENT_COLOR_SPACE_OKLAB);
+ hue_texture->set_gradient(hue_gradient);
+ }
- Ref hue_gradient;
- hue_gradient.instantiate();
- PackedFloat32Array offsets;
- offsets.resize(precision);
PackedColorArray colors;
colors.resize(precision);
for (int i = 0; i < precision; i++) {
float h = i / float(precision - 1);
- offsets.write[i] = h;
- colors.write[i] = Color::from_ok_hsl(h, color.get_ok_hsl_s(), color.get_ok_hsl_l());
+ colors.write[i] = Color::from_ok_hsl(h, slider_sat, okhsl_l);
}
- hue_gradient->set_offsets(offsets);
hue_gradient->set_colors(colors);
- hue_gradient->set_interpolation_color_space(Gradient::ColorSpace::GRADIENT_COLOR_SPACE_OKLAB);
- if (hue_texture.is_null()) {
- hue_texture.instantiate();
- hue_texture->set_width(800);
- hue_texture->set_height(6);
- }
- hue_texture->set_gradient(hue_gradient);
slider->draw_texture_rect(hue_texture, Rect2(Vector2(), Vector2(size.x, margin)), false);
}
}
diff --git a/scene/gui/color_mode.h b/scene/gui/color_mode.h
index 9c5ffe6fe90..64863a9cf25 100644
--- a/scene/gui/color_mode.h
+++ b/scene/gui/color_mode.h
@@ -48,12 +48,15 @@ public:
virtual bool get_allow_greater() const { return false; }
virtual float get_slider_value(int idx) const = 0;
+ virtual float get_alpha_slider_max() const { return 255.0; }
+ virtual float get_alpha_slider_value() const { return color_picker->get_pick_color().a * 255.0; }
+
virtual Color get_color() const = 0;
virtual void _value_changed() {}
+ virtual void _greater_value_inputted() {}
virtual void slider_draw(int p_which) = 0;
- virtual bool apply_theme() const { return false; }
ColorMode(ColorPicker *p_color_picker);
virtual ~ColorMode() {}
@@ -62,7 +65,7 @@ public:
class ColorModeHSV : public ColorMode {
public:
String labels[3] = { "H", "S", "V" };
- float slider_max[4] = { 359, 100, 100, 255 };
+ float slider_max[3] = { 359, 100, 100 };
float cached_hue = 0.0;
float cached_saturation = 0.0;
@@ -86,6 +89,7 @@ public:
class ColorModeRGB : public ColorMode {
public:
String labels[3] = { "R", "G", "B" };
+ Ref rgb_texture[3];
virtual String get_name() const override { return "RGB"; }
@@ -97,18 +101,21 @@ public:
virtual Color get_color() const override;
+ virtual void _greater_value_inputted() override;
+
virtual void slider_draw(int p_which) override;
ColorModeRGB(ColorPicker *p_color_picker) :
ColorMode(p_color_picker) {}
};
-class ColorModeRAW : public ColorMode {
+class ColorModeLinear : public ColorMode {
public:
String labels[3] = { "R", "G", "B" };
- float slider_max[4] = { 100, 100, 100, 1 };
+ float slider_max[3] = { 1, 1, 1 };
+ Ref rgb_texture[3];
- virtual String get_name() const override { return "RAW"; }
+ virtual String get_name() const override { return "Linear"; }
virtual float get_slider_step() const override { return 0.001; }
virtual float get_spinbox_arrow_step() const override { return 0.01; }
@@ -117,19 +124,23 @@ public:
virtual bool get_allow_greater() const override { return true; }
virtual float get_slider_value(int idx) const override;
+ virtual float get_alpha_slider_max() const override;
+ virtual float get_alpha_slider_value() const override;
+
virtual Color get_color() const override;
- virtual void slider_draw(int p_which) override;
- virtual bool apply_theme() const override;
+ virtual void _greater_value_inputted() override;
- ColorModeRAW(ColorPicker *p_color_picker) :
+ virtual void slider_draw(int p_which) override;
+
+ ColorModeLinear(ColorPicker *p_color_picker) :
ColorMode(p_color_picker) {}
};
class ColorModeOKHSL : public ColorMode {
public:
String labels[3] = { "H", "S", "L" };
- float slider_max[4] = { 359, 100, 100, 255 };
+ float slider_max[3] = { 359, 100, 100 };
float cached_hue = 0.0;
float cached_saturation = 0.0;
Ref hue_texture;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index ad781197221..16421c848b4 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -31,6 +31,7 @@
#include "color_picker.h"
#include "core/io/image.h"
+#include "core/math/expression.h"
#include "scene/gui/color_mode.h"
#include "scene/gui/color_picker_shape.h"
#include "scene/gui/file_dialog.h"
@@ -53,6 +54,27 @@
#include "scene/theme/theme_db.h"
#include "thirdparty/misc/ok_color_shader.h"
+static inline bool is_color_overbright(const Color &color) {
+ return (color.r > 1.0) || (color.g > 1.0) || (color.b > 1.0);
+}
+
+static inline bool is_color_valid_hex(const Color &color) {
+ return !is_color_overbright(color) && color.r >= 0 && color.g >= 0 && color.b >= 0;
+}
+
+static inline String color_to_string(const Color &color, bool show_alpha = true, bool force_value_format = false) {
+ if (!force_value_format && !is_color_overbright(color)) {
+ return "#" + color.to_html(show_alpha);
+ }
+ String t = "(" + String::num(color.r, 3) + ", " + String::num(color.g, 3) + ", " + String::num(color.b, 3);
+ if (show_alpha) {
+ t += ", " + String::num(color.a, 3) + ")";
+ } else {
+ t += ")";
+ }
+ return t;
+}
+
void ColorPicker::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
@@ -126,6 +148,7 @@ void ColorPicker::_notification(int p_what) {
}
alpha_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
alpha_slider->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
+ intensity_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
for (int i = 0; i < MODE_BUTTON_COUNT; i++) {
mode_btns[i]->begin_bulk_theme_override();
@@ -144,10 +167,9 @@ void ColorPicker::_notification(int p_what) {
_reset_sliders_theme();
- if (Engine::get_singleton()->is_editor_hint()) {
- // Adjust for the width of the "Script" icon.
- text_type->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
- }
+ hex_label->set_custom_minimum_size(Size2(38 * theme_cache.base_scale, 0));
+ // Adjust for the width of the "script" icon.
+ text_type->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
_update_presets();
_update_recent_presets();
@@ -348,17 +370,17 @@ void ColorPicker::_update_controls() {
alpha_slider->set_accessibility_name(ETR("Alpha"));
alpha_value->set_accessibility_name(ETR("Alpha"));
- slider_theme_modified = modes[current_mode]->apply_theme();
+ intensity_label->set_text("I");
+ intensity_slider->set_accessibility_name(ETR("Intensity"));
+ intensity_value->set_accessibility_name(ETR("Intensity"));
- if (edit_alpha) {
- alpha_value->show();
- alpha_slider->show();
- alpha_label->show();
- } else {
- alpha_value->hide();
- alpha_slider->hide();
- alpha_label->hide();
- }
+ alpha_value->set_visible(edit_alpha);
+ alpha_slider->set_visible(edit_alpha);
+ alpha_label->set_visible(edit_alpha);
+
+ intensity_value->set_visible(edit_intensity);
+ intensity_slider->set_visible(edit_intensity);
+ intensity_label->set_visible(edit_intensity);
int i = 0;
for (ColorPickerShape *shape : shapes) {
@@ -381,17 +403,17 @@ void ColorPicker::_update_controls() {
btn_shape->set_visible(current_shape != SHAPE_NONE);
}
-void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) {
+void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders, bool p_calc_intensity) {
if (text_changed) {
add_recent_preset(color);
text_changed = false;
}
color = p_color;
- if (color != last_color) {
- _copy_color_to_hsv();
- last_color = color;
+ if (p_calc_intensity) {
+ _copy_color_to_normalized_and_intensity();
}
+ _copy_normalized_to_hsv_okhsl();
if (!is_inside_tree()) {
return;
@@ -401,7 +423,7 @@ void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) {
}
void ColorPicker::set_pick_color(const Color &p_color) {
- _set_pick_color(p_color, true); //because setters can't have more arguments
+ _set_pick_color(p_color, true, true); // Because setters can't have more arguments.
}
void ColorPicker::set_old_color(const Color &p_color) {
@@ -435,6 +457,32 @@ bool ColorPicker::is_editing_alpha() const {
return edit_alpha;
}
+void ColorPicker::set_edit_intensity(bool p_show) {
+ if (edit_intensity == p_show) {
+ return;
+ }
+ if (p_show) {
+ set_pick_color(color);
+ } else {
+ _normalized_apply_intensity_to_color();
+ color_normalized = color;
+ intensity = 0;
+ }
+ edit_intensity = p_show;
+ _update_controls();
+
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ _update_color();
+ sample->queue_redraw();
+}
+
+bool ColorPicker::is_editing_intensity() const {
+ return edit_intensity;
+}
+
void ColorPicker::_slider_drag_started() {
currently_dragging = true;
}
@@ -444,32 +492,18 @@ void ColorPicker::_slider_value_changed() {
return;
}
- color = modes[current_mode]->get_color();
+ intensity = intensity_value->get_value();
+ color_normalized = modes[current_mode]->get_color();
+ if (edit_intensity && is_color_overbright(color_normalized)) {
+ modes[current_mode]->_greater_value_inputted();
+ color_normalized = modes[current_mode]->get_color();
+ }
+ _normalized_apply_intensity_to_color();
+ intensity_value->set_prefix(intensity < 0 ? "" : "+");
+
modes[current_mode]->_value_changed();
- if (current_mode == MODE_HSV) {
- h = sliders[0]->get_value() / 360.0;
- s = sliders[1]->get_value() / 100.0;
- v = sliders[2]->get_value() / 100.0;
- ok_hsl_h = color.get_ok_hsl_h();
- ok_hsl_s = color.get_ok_hsl_s();
- ok_hsl_l = color.get_ok_hsl_l();
-
- circle_keyboard_joypad_picker_cursor_position = Vector2i();
- last_color = color;
- } else if (current_mode == MODE_OKHSL) {
- ok_hsl_h = sliders[0]->get_value() / 360.0;
- ok_hsl_s = sliders[1]->get_value() / 100.0;
- ok_hsl_l = sliders[2]->get_value() / 100.0;
- h = color.get_h();
- s = color.get_s();
- v = color.get_v();
-
- circle_keyboard_joypad_picker_cursor_position = Vector2i();
- last_color = color;
- }
-
- _set_pick_color(color, false);
+ _set_pick_color(color, false, false);
if (!deferred_mode_enabled || !currently_dragging) {
emit_signal(SNAME("color_changed"), color);
}
@@ -518,14 +552,22 @@ void ColorPicker::create_slider(GridContainer *gc, int idx) {
slider->connect("drag_started", callable_mp(this, &ColorPicker::_slider_drag_started));
slider->connect(SceneStringName(value_changed), callable_mp(this, &ColorPicker::_slider_value_changed).unbind(1));
slider->connect("drag_ended", callable_mp(this, &ColorPicker::_slider_drag_ended).unbind(1));
- slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_slider_draw).bind(idx));
+ if (idx < SLIDER_COUNT) {
+ slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_slider_draw).bind(idx));
+ } else if (idx == SLIDER_ALPHA) {
+ slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_alpha_slider_draw));
+ }
slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPicker::_slider_or_spin_input));
if (idx < SLIDER_COUNT) {
sliders[idx] = slider;
values[idx] = val;
labels[idx] = lbl;
- } else {
+ } else if (idx == SLIDER_INTENSITY) {
+ intensity_slider = slider;
+ intensity_value = val;
+ intensity_label = lbl;
+ } else if (idx == SLIDER_ALPHA) {
alpha_slider = slider;
alpha_value = val;
alpha_label = lbl;
@@ -591,23 +633,55 @@ Vector ColorPicker::get_active_slider_values() {
return cur_values;
}
-void ColorPicker::_copy_color_to_hsv() {
- ok_hsl_h = color.get_ok_hsl_h();
- ok_hsl_s = color.get_ok_hsl_s();
- ok_hsl_l = color.get_ok_hsl_l();
- h = color.get_h();
- s = color.get_s();
- v = color.get_v();
+void ColorPicker::_copy_normalized_to_hsv_okhsl() {
+ if (!okhsl_cached) {
+ ok_hsl_h = color_normalized.get_ok_hsl_h();
+ ok_hsl_s = color_normalized.get_ok_hsl_s();
+ ok_hsl_l = color_normalized.get_ok_hsl_l();
+ }
+ if (!hsv_cached) {
+ h = color_normalized.get_h();
+ s = color_normalized.get_s();
+ v = color_normalized.get_v();
+ }
+ hsv_cached = false;
+ okhsl_cached = false;
}
-void ColorPicker::_copy_hsv_to_color() {
+void ColorPicker::_copy_hsv_okhsl_to_normalized() {
if (current_shape != SHAPE_NONE && shapes[current_shape]->is_ok_hsl()) {
- color.set_ok_hsl(ok_hsl_h, ok_hsl_s, ok_hsl_l, color.a);
+ color_normalized.set_ok_hsl(ok_hsl_h, ok_hsl_s, ok_hsl_l, color_normalized.a);
} else {
- color.set_hsv(h, s, v, color.a);
+ color_normalized.set_hsv(h, s, v, color_normalized.a);
}
}
+Color ColorPicker::_color_apply_intensity(const Color &col) const {
+ Color linear_color = col.srgb_to_linear();
+ Color result;
+ float multiplier = Math::pow(2, intensity);
+ for (int i = 0; i < 3; i++) {
+ result.components[i] = linear_color.components[i] * multiplier;
+ }
+ result.a = col.a;
+ return result.linear_to_srgb();
+}
+
+void ColorPicker::_normalized_apply_intensity_to_color() {
+ color = _color_apply_intensity(color_normalized);
+}
+
+void ColorPicker::_copy_color_to_normalized_and_intensity() {
+ Color linear_color = color.srgb_to_linear();
+ float multiplier = MAX(1, MAX(MAX(linear_color.r, linear_color.g), linear_color.b));
+ for (int i = 0; i < 3; i++) {
+ color_normalized.components[i] = linear_color.components[i] / multiplier;
+ }
+ color_normalized.a = linear_color.a;
+ color_normalized = color_normalized.linear_to_srgb();
+ intensity = Math::log2(multiplier);
+}
+
void ColorPicker::_select_from_preset_container(const Color &p_color) {
if (preset_group->get_pressed_button()) {
preset_group->get_pressed_button()->set_pressed(false);
@@ -660,35 +734,52 @@ void ColorPicker::_reset_sliders_theme() {
}
void ColorPicker::_html_submitted(const String &p_html) {
- if (updating || text_is_constructor || !c_text->is_visible()) {
+ if (updating) {
return;
}
-
- Color new_color = Color::from_string(p_html.strip_edges(), color);
- String html_no_prefix = p_html.strip_edges().trim_prefix("#");
- if (html_no_prefix.is_valid_hex_number(false)) {
- // Convert invalid HTML color codes that software like Figma supports.
- if (html_no_prefix.length() == 1) {
- // Turn `#1` into `#111111`.
- html_no_prefix = html_no_prefix.repeat(6);
- } else if (html_no_prefix.length() == 2) {
- // Turn `#12` into `#121212`.
- html_no_prefix = html_no_prefix.repeat(3);
- } else if (html_no_prefix.length() == 5) {
- // Turn `#12345` into `#11223344`.
- html_no_prefix = html_no_prefix.left(4);
- } else if (html_no_prefix.length() == 7) {
- // Turn `#1234567` into `#123456`.
- html_no_prefix = html_no_prefix.left(6);
+ Color new_color = color;
+ if (text_is_constructor || !is_color_valid_hex(color)) {
+ Ref expr;
+ expr.instantiate();
+ Error err = expr->parse(p_html);
+ if (err == OK) {
+ Variant result = expr->execute(Array(), nullptr, false, true);
+ // This is basically the same as Variant::operator Color(), but remains original color if Color::from_string() fails
+ if (result.get_type() == Variant::COLOR) {
+ new_color = result;
+ } else if (result.get_type() == Variant::STRING) {
+ new_color = Color::from_string(result, color);
+ } else if (result.get_type() == Variant::INT) {
+ new_color = Color::hex(result);
+ }
}
+ } else {
+ new_color = Color::from_string(p_html.strip_edges(), color);
+ String html_no_prefix = p_html.strip_edges().trim_prefix("#");
+ if (html_no_prefix.is_valid_hex_number(false)) {
+ // Convert invalid HTML color codes that software like Figma supports.
+ if (html_no_prefix.length() == 1) {
+ // Turn `#1` into `#111111`.
+ html_no_prefix = html_no_prefix.repeat(6);
+ } else if (html_no_prefix.length() == 2) {
+ // Turn `#12` into `#121212`.
+ html_no_prefix = html_no_prefix.repeat(3);
+ } else if (html_no_prefix.length() == 5) {
+ // Turn `#12345` into `#11223344`.
+ html_no_prefix = html_no_prefix.left(4);
+ } else if (html_no_prefix.length() == 7) {
+ // Turn `#1234567` into `#123456`.
+ html_no_prefix = html_no_prefix.left(6);
+ }
+ }
+ new_color = Color::from_string(html_no_prefix, new_color);
}
- new_color = Color::from_string(html_no_prefix, new_color);
if (!is_editing_alpha()) {
new_color.a = color.a;
}
- if (new_color.to_argb32() == color.to_argb32()) {
+ if (new_color == color) {
return;
}
color = new_color;
@@ -714,9 +805,11 @@ void ColorPicker::_update_color(bool p_update_sliders) {
values[i]->set_custom_arrow_step(spinbox_arrow_step);
values[i]->set_allow_greater(modes[current_mode]->get_allow_greater());
}
- alpha_slider->set_max(modes[current_mode]->get_slider_max(current_slider_count));
+ alpha_slider->set_max(modes[current_mode]->get_alpha_slider_max());
alpha_slider->set_step(step);
- alpha_slider->set_value(modes[current_mode]->get_slider_value(current_slider_count));
+ alpha_slider->set_value(modes[current_mode]->get_alpha_slider_value());
+ intensity_slider->set_value(intensity);
+ intensity_value->set_prefix(intensity < 0 ? "" : "+");
}
_update_text_value();
@@ -808,16 +901,16 @@ void ColorPicker::_update_recent_presets() {
void ColorPicker::_text_type_toggled() {
text_is_constructor = !text_is_constructor;
if (text_is_constructor) {
+ hex_label->set_text(ETR("Expr"));
text_type->set_text("");
- text_type->set_button_icon(get_editor_theme_icon(SNAME("Script")));
+ text_type->set_button_icon(theme_cache.color_script);
- c_text->set_editable(false);
- c_text->set_tooltip_text(TTRC("Copy this constructor in a script."));
+ c_text->set_tooltip_text(RTR("Execute an expression as a color."));
} else {
+ hex_label->set_text(ETR("Hex"));
text_type->set_text("#");
text_type->set_button_icon(nullptr);
- c_text->set_editable(true);
c_text->set_tooltip_text(ETR("Enter a hex code (\"#ff0000\") or named color (\"red\")."));
}
_update_color();
@@ -845,7 +938,6 @@ void ColorPicker::set_picker_shape(PickerShapeType p_shape) {
btn_shape->set_button_icon(shape_popup->get_item_icon(p_shape));
}
- circle_keyboard_joypad_picker_cursor_position = Vector2i();
current_shape = p_shape;
#ifdef TOOLS_ENABLED
@@ -854,8 +946,6 @@ void ColorPicker::set_picker_shape(PickerShapeType p_shape) {
}
#endif
- _copy_color_to_hsv();
-
_update_controls();
_update_color();
}
@@ -1012,7 +1102,6 @@ void ColorPicker::_set_mode_popup_value(ColorModeType p_mode) {
} else {
set_color_mode(p_mode);
}
- circle_keyboard_joypad_picker_cursor_position = Vector2i();
}
Variant ColorPicker::_get_drag_data_fw(const Point2 &p_point, Control *p_from_control) {
@@ -1215,10 +1304,6 @@ void ColorPicker::set_color_mode(ColorModeType p_mode) {
return;
}
- if (slider_theme_modified) {
- _reset_sliders_theme();
- }
-
mode_popup->set_item_checked(current_mode, false);
mode_popup->set_item_checked(p_mode, true);
@@ -1259,22 +1344,20 @@ void ColorPicker::set_colorize_sliders(bool p_colorize_sliders) {
if (colorize_sliders) {
Ref style_box_empty(memnew(StyleBoxEmpty));
- if (!slider_theme_modified) {
- for (int i = 0; i < SLIDER_COUNT; i++) {
- sliders[i]->add_theme_style_override("slider", style_box_empty);
- }
+ for (int i = 0; i < SLIDER_COUNT; i++) {
+ sliders[i]->add_theme_style_override("slider", style_box_empty);
}
+
alpha_slider->add_theme_style_override("slider", style_box_empty);
} else {
Ref style_box_flat(memnew(StyleBoxFlat));
style_box_flat->set_content_margin(SIDE_TOP, 16 * theme_cache.base_scale);
style_box_flat->set_bg_color(Color(0.2, 0.23, 0.31).lerp(Color(0, 0, 0, 1), 0.3).clamp());
- if (!slider_theme_modified) {
- for (int i = 0; i < SLIDER_COUNT; i++) {
- sliders[i]->add_theme_style_override("slider", style_box_flat);
- }
+ for (int i = 0; i < SLIDER_COUNT; i++) {
+ sliders[i]->add_theme_style_override("slider", style_box_flat);
}
+
alpha_slider->add_theme_style_override("slider", style_box_flat);
}
}
@@ -1292,25 +1375,21 @@ bool ColorPicker::is_deferred_mode() const {
}
void ColorPicker::_update_text_value() {
- bool text_visible = true;
- if (text_is_constructor) {
- String t = "Color(" + String::num(color.r, 3) + ", " + String::num(color.g, 3) + ", " + String::num(color.b, 3);
- if (edit_alpha && color.a < 1) {
- t += ", " + String::num(color.a, 3) + ")";
- } else {
- t += ")";
- }
- c_text->set_text(t);
- }
+ if (text_is_constructor || !is_color_valid_hex(color)) {
+ String t = "Color" + color_to_string(color, edit_alpha && color.a < 1, true);
- if (color.r > 1 || color.g > 1 || color.b > 1 || color.r < 0 || color.g < 0 || color.b < 0) {
- text_visible = false;
- } else if (!text_is_constructor) {
+ text_type->set_text("");
+ text_type->set_button_icon(theme_cache.color_script);
+ text_type->set_disabled(!is_color_valid_hex(color));
+ hex_label->set_text(ETR("Expr"));
+ c_text->set_text(t);
+ } else {
+ text_type->set_text("#");
+ text_type->set_button_icon(nullptr);
+ text_type->set_disabled(false);
+ hex_label->set_text(ETR("Hex"));
c_text->set_text(color.to_html(edit_alpha && color.a < 1));
}
-
- text_type->set_visible(text_visible);
- c_text->set_visible(text_visible);
}
void ColorPicker::_sample_input(const Ref &p_event) {
@@ -1366,7 +1445,7 @@ void ColorPicker::_sample_draw() {
sample->set_focus_mode(FOCUS_NONE);
}
- if (old_color.r > 1 || old_color.g > 1 || old_color.b > 1) {
+ if (is_color_overbright(color)) {
// Draw an indicator to denote that the old color is "overbright" and can't be displayed accurately in the preview.
sample->draw_texture(theme_cache.overbright_indicator, Point2());
}
@@ -1385,7 +1464,7 @@ void ColorPicker::_sample_draw() {
theme_cache.sample_focus->draw(ci, rect_old);
}
- if (color.r > 1 || color.g > 1 || color.b > 1) {
+ if (is_color_overbright(color)) {
// Draw an indicator to denote that the new color is "overbright" and can't be displayed accurately in the preview.
sample->draw_texture(theme_cache.overbright_indicator, Point2(sample->get_size().width * 0.5, 0));
}
@@ -1397,6 +1476,37 @@ void ColorPicker::_slider_draw(int p_which) {
}
}
+void ColorPicker::_alpha_slider_draw() {
+ if (!colorize_sliders) {
+ return;
+ }
+ Vector pos;
+ pos.resize(4);
+ Vector col;
+ col.resize(4);
+ Size2 size = alpha_slider->get_size();
+ Color left_color;
+ Color right_color;
+ const real_t margin = 16 * theme_cache.base_scale;
+ alpha_slider->draw_texture_rect(theme_cache.sample_bg, Rect2(Point2(0, 0), Size2(size.x, margin)), true);
+
+ left_color = color_normalized;
+ left_color.a = 0;
+ right_color = color_normalized;
+ right_color.a = 1;
+
+ col.set(0, left_color);
+ col.set(1, right_color);
+ col.set(2, right_color);
+ col.set(3, left_color);
+ pos.set(0, Vector2(0, 0));
+ pos.set(1, Vector2(size.x, 0));
+ pos.set(2, Vector2(size.x, margin));
+ pos.set(3, Vector2(0, margin));
+
+ alpha_slider->draw_polygon(pos, col);
+}
+
void ColorPicker::_slider_or_spin_input(const Ref &p_event) {
if (line_edit_mouse_release) {
line_edit_mouse_release = false;
@@ -1443,7 +1553,11 @@ void ColorPicker::_recent_preset_pressed(const bool p_pressed, ColorPresetButton
if (!p_pressed) {
return;
}
- set_pick_color(p_preset->get_preset_color());
+
+ // Avoid applying and recalculating the intensity for non-overbright color if it doesn't change.
+ if (color != p_preset->get_preset_color()) {
+ set_pick_color(p_preset->get_preset_color());
+ }
recent_presets.move_to_back(recent_presets.find(p_preset->get_preset_color()));
List::Element *e = recent_preset_cache.find(p_preset->get_preset_color());
@@ -1820,7 +1934,6 @@ void ColorPicker::_html_focus_exit() {
} else {
_update_text_value();
}
- circle_keyboard_joypad_picker_cursor_position = Vector2i();
}
void ColorPicker::set_can_add_swatches(bool p_enabled) {
@@ -1910,6 +2023,8 @@ void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_color_mode"), &ColorPicker::get_color_mode);
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPicker::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPicker::is_editing_alpha);
+ ClassDB::bind_method(D_METHOD("set_edit_intensity", "show"), &ColorPicker::set_edit_intensity);
+ ClassDB::bind_method(D_METHOD("is_editing_intensity"), &ColorPicker::is_editing_intensity);
ClassDB::bind_method(D_METHOD("set_can_add_swatches", "enabled"), &ColorPicker::set_can_add_swatches);
ClassDB::bind_method(D_METHOD("are_swatches_enabled"), &ColorPicker::are_swatches_enabled);
ClassDB::bind_method(D_METHOD("set_presets_visible", "visible"), &ColorPicker::set_presets_visible);
@@ -1933,7 +2048,8 @@ void ColorPicker::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "color_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW,OKHSL"), "set_color_mode", "get_color_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_intensity"), "set_edit_intensity", "is_editing_intensity");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "color_mode", PROPERTY_HINT_ENUM, "RGB,HSV,LINEAR,OKHSL"), "set_color_mode", "get_color_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deferred_mode"), "set_deferred_mode", "is_deferred_mode");
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");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_add_swatches"), "set_can_add_swatches", "are_swatches_enabled");
@@ -1950,7 +2066,10 @@ void ColorPicker::_bind_methods() {
BIND_ENUM_CONSTANT(MODE_RGB);
BIND_ENUM_CONSTANT(MODE_HSV);
+#ifndef DISABLE_DEPRECATED
BIND_ENUM_CONSTANT(MODE_RAW);
+#endif
+ BIND_ENUM_CONSTANT(MODE_LINEAR);
BIND_ENUM_CONSTANT(MODE_OKHSL);
BIND_ENUM_CONSTANT(SHAPE_HSV_RECTANGLE);
@@ -1990,6 +2109,8 @@ void ColorPicker::_bind_methods() {
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, picker_cursor_bg);
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, color_hue);
+ BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, ColorPicker, color_script);
+
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_normal, "tab_unselected", "TabContainer");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_pressed, "tab_selected", "TabContainer");
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, ColorPicker, mode_button_hover, "tab_selected", "TabContainer");
@@ -2049,7 +2170,7 @@ ColorPicker::ColorPicker() {
add_mode(memnew(ColorModeRGB(this)));
add_mode(memnew(ColorModeHSV(this)));
- add_mode(memnew(ColorModeRAW(this)));
+ add_mode(memnew(ColorModeLinear(this)));
add_mode(memnew(ColorModeOKHSL(this)));
mode_hbc = memnew(HBoxContainer);
@@ -2099,17 +2220,23 @@ ColorPicker::ColorPicker() {
slider_gc->set_h_size_flags(SIZE_EXPAND_FILL);
slider_gc->set_columns(3);
- for (int i = 0; i < SLIDER_COUNT + 1; i++) {
+ for (int i = 0; i < SLIDER_MAX; i++) {
create_slider(slider_gc, i);
}
-
alpha_label->set_text("A");
+ intensity_label->set_text("I");
+ intensity_slider->set_min(-10);
+ intensity_slider->set_max(10);
+ intensity_slider->set_step(0.001);
+ intensity_value->set_allow_greater(true);
+ intensity_value->set_custom_arrow_step(1);
+
hex_hbc = memnew(HBoxContainer);
hex_hbc->set_alignment(ALIGNMENT_BEGIN);
real_vbox->add_child(hex_hbc);
-
- hex_hbc->add_child(memnew(Label(ETR("Hex"))));
+ hex_label = memnew(Label(ETR("Hex")));
+ hex_hbc->add_child(hex_label);
text_type = memnew(Button);
hex_hbc->add_child(text_type);
@@ -2128,8 +2255,6 @@ ColorPicker::ColorPicker() {
text_type->set_accessibility_name(ETR("Hexadecimal Values"));
#endif // TOOLS_ENABLED
text_type->set_flat(true);
- text_type->set_focus_mode(FOCUS_NONE);
- text_type->set_mouse_filter(MOUSE_FILTER_IGNORE);
}
c_text = memnew(LineEdit);
@@ -2376,6 +2501,20 @@ bool ColorPickerButton::is_editing_alpha() const {
return edit_alpha;
}
+void ColorPickerButton::set_edit_intensity(bool p_show) {
+ if (edit_intensity == p_show) {
+ return;
+ }
+ edit_intensity = p_show;
+ if (picker) {
+ picker->set_edit_intensity(p_show);
+ }
+}
+
+bool ColorPickerButton::is_editing_intensity() const {
+ return edit_intensity;
+}
+
ColorPicker *ColorPickerButton::get_picker() {
_update_picker();
return picker;
@@ -2400,6 +2539,7 @@ void ColorPickerButton::_update_picker() {
picker->connect(SceneStringName(minimum_size_changed), callable_mp((Window *)popup, &Window::reset_size));
picker->set_pick_color(color);
picker->set_edit_alpha(edit_alpha);
+ picker->set_edit_intensity(edit_intensity);
picker->set_display_old_color(true);
emit_signal(SNAME("picker_created"));
}
@@ -2412,6 +2552,8 @@ void ColorPickerButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_popup"), &ColorPickerButton::get_popup);
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPickerButton::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPickerButton::is_editing_alpha);
+ ClassDB::bind_method(D_METHOD("set_edit_intensity", "show"), &ColorPickerButton::set_edit_intensity);
+ ClassDB::bind_method(D_METHOD("is_editing_intensity"), &ColorPickerButton::is_editing_intensity);
ClassDB::bind_method(D_METHOD("_about_to_popup"), &ColorPickerButton::_about_to_popup);
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
@@ -2419,6 +2561,7 @@ void ColorPickerButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("picker_created"));
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_intensity"), "set_edit_intensity", "is_editing_intensity");
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ColorPickerButton, normal_style, "normal");
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, ColorPickerButton, background_icon, "bg");
@@ -2488,7 +2631,7 @@ void ColorPresetButton::_notification(int p_what) {
theme_cache.focus_style->draw(ci, Rect2(Point2(), get_size()));
}
- if (preset_color.r > 1 || preset_color.g > 1 || preset_color.b > 1) {
+ if (is_color_overbright(preset_color)) {
// Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview
draw_texture(theme_cache.overbright_indicator, Vector2(0, 0));
}
@@ -2509,9 +2652,9 @@ Color ColorPresetButton::get_preset_color() const {
String ColorPresetButton::get_tooltip(const Point2 &p_pos) const {
Color color = get_preset_color();
if (recent) {
- return vformat(atr(ETR("Color: #%s\nLMB: Apply color")), color.to_html(color.a < 1));
+ return vformat(atr(ETR("Color: %s\nLMB: Apply color")), color_to_string(color, color.a < 1));
}
- return vformat(atr(ETR("Color: #%s\nLMB: Apply color\nRMB: Remove preset")), color.to_html(color.a < 1));
+ return vformat(atr(ETR("Color: %s\nLMB: Apply color\nRMB: Remove preset")), color_to_string(color, color.a < 1));
}
void ColorPresetButton::_bind_methods() {
@@ -2526,7 +2669,7 @@ ColorPresetButton::ColorPresetButton(Color p_color, int p_size, bool p_recent) {
recent = p_recent;
set_toggle_mode(true);
set_custom_minimum_size(Size2(p_size, p_size));
- set_accessibility_name(vformat(atr(ETR("Color: #%s")), p_color.to_html(p_color.a < 1)));
+ set_accessibility_name(vformat(atr(ETR("Color: %s")), color_to_string(p_color, p_color.a < 1)));
set_tooltip_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 42abedea1a2..9a1f212abee 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -91,14 +91,17 @@ class ColorPicker : public VBoxContainer {
friend class ColorModeRGB;
friend class ColorModeHSV;
- friend class ColorModeRAW;
+ friend class ColorModeLinear;
friend class ColorModeOKHSL;
public:
enum ColorModeType {
MODE_RGB,
MODE_HSV,
- MODE_RAW,
+#ifndef DISABLE_DEPRECATED
+ MODE_RAW = 2,
+#endif
+ MODE_LINEAR = 2,
MODE_OKHSL,
MODE_MAX
@@ -115,6 +118,12 @@ public:
};
static const int SLIDER_COUNT = 3;
+ enum SLIDER_EXTRA {
+ SLIDER_INTENSITY = 3,
+ SLIDER_ALPHA,
+
+ SLIDER_MAX
+ };
private:
enum class MenuOption {
@@ -136,7 +145,6 @@ private:
#endif
int current_slider_count = SLIDER_COUNT;
- Vector2i circle_keyboard_joypad_picker_cursor_position;
const float DEFAULT_GAMEPAD_EVENT_DELAY_MS = 1.0 / 2;
const float GAMEPAD_EVENT_REPEAT_RATE_MS = 1.0 / 30;
@@ -182,6 +190,7 @@ private:
HBoxContainer *sample_hbc = nullptr;
GridContainer *slider_gc = nullptr;
HBoxContainer *hex_hbc = nullptr;
+ Label *hex_label = nullptr;
MenuButton *btn_mode = nullptr;
Button *mode_btns[MODE_BUTTON_COUNT];
Ref mode_group;
@@ -199,9 +208,10 @@ private:
OptionButton *mode_option_button = nullptr;
- HSlider *sliders[SLIDER_COUNT];
- SpinBox *values[SLIDER_COUNT];
- Label *labels[SLIDER_COUNT];
+ HSlider *sliders[SLIDER_MAX];
+ SpinBox *values[SLIDER_MAX];
+ Label *labels[SLIDER_MAX];
+
Button *text_type = nullptr;
LineEdit *c_text = nullptr;
@@ -210,6 +220,13 @@ private:
Label *alpha_label = nullptr;
bool edit_alpha = true;
+
+ HSlider *intensity_slider = nullptr;
+ SpinBox *intensity_value = nullptr;
+ Label *intensity_label = nullptr;
+
+ bool edit_intensity = true;
+
Size2i ms;
bool text_is_constructor = false;
PickerShapeType current_shape = SHAPE_HSV_RECTANGLE;
@@ -223,6 +240,7 @@ private:
List recent_presets;
Color color;
+ Color color_normalized;
Color old_color;
Color pre_picking_color;
bool is_picking_color = false;
@@ -250,7 +268,10 @@ private:
float ok_hsl_s = 0.0;
float ok_hsl_l = 0.0;
- Color last_color;
+ bool hsv_cached = false;
+ bool okhsl_cached = false;
+
+ float intensity = 0.0;
struct ThemeCache {
float base_scale = 1.0;
@@ -286,14 +307,20 @@ private:
Ref picker_cursor_bg;
Ref color_hue;
+ Ref color_script;
+
/* Mode buttons */
Ref mode_button_normal;
Ref mode_button_pressed;
Ref mode_button_hover;
} theme_cache;
- void _copy_color_to_hsv();
- void _copy_hsv_to_color();
+ void _copy_normalized_to_hsv_okhsl();
+ void _copy_hsv_okhsl_to_normalized();
+
+ Color _color_apply_intensity(const Color &col) const;
+ void _normalized_apply_intensity_to_color();
+ void _copy_color_to_normalized_and_intensity();
void create_slider(GridContainer *gc, int idx);
void _reset_sliders_theme();
@@ -310,6 +337,7 @@ private:
void _sample_input(const Ref &p_event);
void _sample_draw();
void _slider_draw(int p_which);
+ void _alpha_slider_draw();
void _slider_or_spin_input(const Ref &p_event);
void _line_edit_input(const Ref &p_event);
@@ -378,7 +406,10 @@ public:
void set_edit_alpha(bool p_show);
bool is_editing_alpha() const;
- void _set_pick_color(const Color &p_color, bool p_update_sliders);
+ void set_edit_intensity(bool p_show);
+ bool is_editing_intensity() const;
+
+ void _set_pick_color(const Color &p_color, bool p_update_sliders, bool p_calc_intensity);
void set_pick_color(const Color &p_color);
Color get_pick_color() const;
void set_old_color(const Color &p_color);
@@ -453,6 +484,7 @@ class ColorPickerButton : public Button {
ColorPicker *picker = nullptr;
Color color;
bool edit_alpha = true;
+ bool edit_intensity = true;
struct ThemeCache {
Ref normal_style;
@@ -480,6 +512,9 @@ public:
void set_edit_alpha(bool p_show);
bool is_editing_alpha() const;
+ void set_edit_intensity(bool p_show);
+ bool is_editing_intensity() const;
+
ColorPicker *get_picker();
PopupPanel *get_popup();
diff --git a/scene/gui/color_picker_shape.cpp b/scene/gui/color_picker_shape.cpp
index 3e96147489a..5cc2f1a8498 100644
--- a/scene/gui/color_picker_shape.cpp
+++ b/scene/gui/color_picker_shape.cpp
@@ -68,9 +68,11 @@ bool ColorPickerShape::can_handle(const Ref &p_event, Vector2 &r_pos
}
void ColorPickerShape::apply_color() {
- color_picker->_copy_hsv_to_color();
- color_picker->last_color = color_picker->color;
- color_picker->set_pick_color(color_picker->color);
+ color_picker->_copy_hsv_okhsl_to_normalized();
+ color_picker->_normalized_apply_intensity_to_color();
+ color_picker->hsv_cached = true;
+ color_picker->okhsl_cached = true;
+ color_picker->_set_pick_color(color_picker->color, true, false);
if (!color_picker->deferred_mode_enabled) {
_emit_color_changed();
@@ -122,10 +124,8 @@ void ColorPickerShape::draw_sv_square(Control *p_control, const Rect2 &p_square,
Vector2(p_square.position.x, end.y),
};
- Color color1 = color_picker->color;
- color1.set_hsv(color_picker->h, 1, 1);
- Color color2 = color1;
- color2.set_hsv(color_picker->h, 1, 0);
+ Color color1 = Color::from_hsv(color_picker->h, 1, 1);
+ Color color2 = Color::from_hsv(color_picker->h, 1, 0);
PackedColorArray colors = {
Color(1, 1, 1, 1),
diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp
index 17499750854..b82f2721e7b 100644
--- a/scene/theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
@@ -1060,6 +1060,7 @@ void fill_default_theme(Ref &theme, const Ref &default_font, const
theme->set_icon("bar_arrow", "ColorPicker", icons["color_picker_bar_arrow"]);
theme->set_icon("picker_cursor", "ColorPicker", icons["color_picker_cursor"]);
theme->set_icon("picker_cursor_bg", "ColorPicker", icons["color_picker_cursor_bg"]);
+ theme->set_icon("color_script", "ColorPicker", icons["script"]);
{
const int precision = 7;
diff --git a/scene/theme/icons/script.svg b/scene/theme/icons/script.svg
new file mode 100644
index 00000000000..4e418ab68a0
--- /dev/null
+++ b/scene/theme/icons/script.svg
@@ -0,0 +1 @@
+