From 8a9409257b69d16902a902b6e6f99a3d867bf054 Mon Sep 17 00:00:00 2001 From: LuoZhihao Date: Fri, 23 May 2025 12:45:29 +0800 Subject: [PATCH] ColorPicker: Add intensity slider Add intensity slider to all color modes. Replace raw mode by linear mode, which uses linear color space. When color is overbright, automatically switch hex text to script text. Allow executing expression in script text field to set color. Add the "script" icon to the default theme. --- doc/classes/ColorPicker.xml | 14 +- doc/classes/ColorPickerButton.xml | 3 + doc/classes/EditorSettings.xml | 3 + editor/editor_node.cpp | 2 + editor/editor_settings.cpp | 1 + editor/themes/editor_theme_manager.cpp | 1 + scene/gui/color_mode.cpp | 318 +++++++++++--------- scene/gui/color_mode.h | 29 +- scene/gui/color_picker.cpp | 401 +++++++++++++++++-------- scene/gui/color_picker.h | 55 +++- scene/gui/color_picker_shape.cpp | 14 +- scene/theme/default_theme.cpp | 1 + scene/theme/icons/script.svg | 1 + 13 files changed, 545 insertions(+), 298 deletions(-) create mode 100644 scene/theme/icons/script.svg 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 d0cf8017a80..cc9e88cf86b 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -965,6 +965,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 cf729bc9a5d..6a33259d973 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4117,9 +4117,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 a4d5d496210..4376b5bb148 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -558,6 +558,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 b7472a93a08..51b768e929b 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 cb7c95e3857..95008a5f99a 100644 --- a/scene/theme/default_theme.cpp +++ b/scene/theme/default_theme.cpp @@ -1055,6 +1055,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 @@ +