2
0
Эх сурвалжийг харах

Merge pull request #103583 from beicause/color-picker-add-intensity

ColorPicker: Add an intensity slider to all modes for HDR
Thaddeus Crews 3 сар өмнө
parent
commit
482dacc300

+ 11 - 3
doc/classes/ColorPicker.xml

@@ -73,6 +73,9 @@
 		<member name="edit_alpha" type="bool" setter="set_edit_alpha" getter="is_editing_alpha" default="true">
 			If [code]true[/code], shows an alpha channel slider (opacity).
 		</member>
+		<member name="edit_intensity" type="bool" setter="set_edit_intensity" getter="is_editing_intensity" default="true">
+			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.
+		</member>
 		<member name="hex_visible" type="bool" setter="set_hex_visible" getter="is_hex_visible" default="true">
 			If [code]true[/code], the hex color code input field is visible.
 		</member>
@@ -111,13 +114,15 @@
 	</signals>
 	<constants>
 		<constant name="MODE_RGB" value="0" enum="ColorModeType">
-			Allows editing the color with Red/Green/Blue sliders.
+			Allows editing the color with Red/Green/Blue sliders in sRGB color space.
 		</constant>
 		<constant name="MODE_HSV" value="1" enum="ColorModeType">
 			Allows editing the color with Hue/Saturation/Value sliders.
 		</constant>
-		<constant name="MODE_RAW" value="2" enum="ColorModeType">
-			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).
+		<constant name="MODE_RAW" value="2" enum="ColorModeType" deprecated="This is replaced by [constant MODE_LINEAR].">
+		</constant>
+		<constant name="MODE_LINEAR" value="2" enum="ColorModeType">
+			Allows editing the color with Red/Green/Blue sliders in linear color space.
 		</constant>
 		<constant name="MODE_OKHSL" value="3" enum="ColorModeType">
 			Allows editing the color with Hue/Saturation/Lightness sliders.
@@ -171,6 +176,9 @@
 		<theme_item name="color_hue" data_type="icon" type="Texture2D">
 			Custom texture for the hue selection slider on the right.
 		</theme_item>
+		<theme_item name="color_script" data_type="icon" type="Texture2D">
+			The icon for the button that switches color text to hexadecimal.
+		</theme_item>
 		<theme_item name="expanded_arrow" data_type="icon" type="Texture2D">
 			The icon for color preset drop down menu when expanded.
 		</theme_item>

+ 3 - 0
doc/classes/ColorPickerButton.xml

@@ -35,6 +35,9 @@
 		<member name="edit_alpha" type="bool" setter="set_edit_alpha" getter="is_editing_alpha" default="true">
 			If [code]true[/code], the alpha channel in the displayed [ColorPicker] will be visible.
 		</member>
+		<member name="edit_intensity" type="bool" setter="set_edit_intensity" getter="is_editing_intensity" default="true">
+			If [code]true[/code], the intensity slider in the displayed [ColorPicker] will be visible.
+		</member>
 		<member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode" overrides="BaseButton" default="true" />
 	</members>
 	<signals>

+ 3 - 0
doc/classes/EditorSettings.xml

@@ -973,6 +973,9 @@
 		<member name="interface/inspector/auto_unfold_foreign_scenes" type="bool" setter="" getter="">
 			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.
 		</member>
+		<member name="interface/inspector/color_picker_show_intensity" type="bool" setter="" getter="">
+			If [code]true[/code], show the intensity slider in the [ColorPicker]s opened in the editor.
+		</member>
 		<member name="interface/inspector/default_color_picker_mode" type="int" setter="" getter="">
 			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.
 		</member>

+ 2 - 0
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));

+ 1 - 0
editor/editor_settings.cpp

@@ -570,6 +570,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> 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, "")

+ 1 - 0
editor/themes/editor_theme_manager.cpp

@@ -1821,6 +1821,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &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)));

+ 176 - 138
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<Vector2> 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);
+
+	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);
+	}
 
-		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);
+	Ref<GradientTexture2D> gradient_texture = rgb_texture[p_which];
+
+	Ref<Gradient> 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);
 	}
 
-	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));
+	PackedColorArray colors = { left_color, right_color };
+	gradient->set_colors(colors);
 
-	slider->draw_polygon(pos, col);
+	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<float> 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];
+}
+
+float ColorModeLinear::get_alpha_slider_max() const {
+	return 1;
 }
 
-Color ColorModeRAW::get_color() const {
+float ColorModeLinear::get_alpha_slider_value() const {
+	return color_picker->get_pick_color().a;
+}
+
+Color ColorModeLinear::get_color() const {
 	Vector<float> 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<Vector2> pos;
 	pos.resize(4);
 	Vector<Color> 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;
-		left_color.a = 0;
-		right_color = color;
-		right_color.a = 1;
+	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<GradientTexture2D> gradient_texture = rgb_texture[p_which];
 
-		slider->draw_polygon(pos, col);
+	Ref<Gradient> 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);
 	}
-}
 
-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");
-	}
+	PackedColorArray colors = { left_color, right_color };
+	gradient->set_colors(colors);
 
-	return true;
+	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<float> 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<Color> 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<Gradient> 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<Gradient> 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);
 	}
 }

+ 19 - 8
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<GradientTexture2D> 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<GradientTexture2D> 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 _greater_value_inputted() override;
+
 	virtual void slider_draw(int p_which) override;
-	virtual bool apply_theme() const override;
 
-	ColorModeRAW(ColorPicker *p_color_picker) :
+	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<GradientTexture2D> hue_texture;

+ 272 - 129
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,21 +633,53 @@ Vector<float> 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) {
@@ -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<Expression> 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<StyleBoxEmpty> 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<StyleBoxFlat> 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<InputEvent> &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<Vector2> pos;
+	pos.resize(4);
+	Vector<Color> 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<InputEvent> &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<Color>::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);
 }
 

+ 45 - 10
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<ButtonGroup> 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<Color> 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<Texture2D> picker_cursor_bg;
 		Ref<Texture2D> color_hue;
 
+		Ref<Texture2D> color_script;
+
 		/* Mode buttons */
 		Ref<StyleBox> mode_button_normal;
 		Ref<StyleBox> mode_button_pressed;
 		Ref<StyleBox> 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<InputEvent> &p_event);
 	void _sample_draw();
 	void _slider_draw(int p_which);
+	void _alpha_slider_draw();
 
 	void _slider_or_spin_input(const Ref<InputEvent> &p_event);
 	void _line_edit_input(const Ref<InputEvent> &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<StyleBox> 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();
 

+ 7 - 7
scene/gui/color_picker_shape.cpp

@@ -68,9 +68,11 @@ bool ColorPickerShape::can_handle(const Ref<InputEvent> &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),

+ 1 - 0
scene/theme/default_theme.cpp

@@ -1060,6 +1060,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &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;

+ 1 - 0
scene/theme/icons/script.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M6 1a2 2 0 0 0-2 2v7H1v3a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2V5h3V3a2 2 0 0 0-2-2z"/><path fill-opacity=".2" d="M6 1a2 2 0 0 0-2 2v7H1v3a2 2 0 1 0 4 0V3a1 1 0 0 1 2 0v3h5V5H8V3a2 2 0 0 0-2-2zM2 11h2v2a1 1 0 0 1-2 0z"/></svg>