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 @@ +