Bladeren bron

ColorPicker: Add okhsl HS and HL rectangular picker shapes

LuoZhihao 1 maand geleden
bovenliggende
commit
7b4c95e6d8

+ 6 - 0
doc/classes/ColorPicker.xml

@@ -144,6 +144,12 @@
 		<constant name="SHAPE_NONE" value="4" enum="PickerShapeType">
 		<constant name="SHAPE_NONE" value="4" enum="PickerShapeType">
 			The color space shape and the shape select button are hidden. Can't be selected from the shapes popup.
 			The color space shape and the shape select button are hidden. Can't be selected from the shapes popup.
 		</constant>
 		</constant>
+		<constant name="SHAPE_OK_HS_RECTANGLE" value="5" enum="PickerShapeType">
+			OKHSL Color Model rectangle with constant lightness.
+		</constant>
+		<constant name="SHAPE_OK_HL_RECTANGLE" value="6" enum="PickerShapeType">
+			OKHSL Color Model rectangle with constant saturation.
+		</constant>
 	</constants>
 	</constants>
 	<theme_items>
 	<theme_items>
 		<theme_item name="focused_not_editing_cursor_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.275)">
 		<theme_item name="focused_not_editing_cursor_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.275)">

+ 1 - 1
editor/editor_settings.cpp

@@ -569,7 +569,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
 	_initial_set("interface/inspector/resources_to_open_in_new_inspector", open_in_new_inspector_defaults);
 	_initial_set("interface/inspector/resources_to_open_in_new_inspector", open_in_new_inspector_defaults);
 
 
 	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_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::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,OK HS Rectangle:5,OK HL Rectangle") // `SHAPE_NONE` is 4.
 	EDITOR_SETTING_BASIC(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/color_picker_show_intensity", true, "");
 	EDITOR_SETTING_BASIC(Variant::BOOL, PROPERTY_HINT_NONE, "interface/inspector/color_picker_show_intensity", true, "");
 
 
 	// Theme
 	// Theme

+ 2 - 1
editor/plugins/script_text_editor.cpp

@@ -43,6 +43,7 @@
 #include "editor/gui/editor_toaster.h"
 #include "editor/gui/editor_toaster.h"
 #include "editor/plugins/editor_context_menu_plugin.h"
 #include "editor/plugins/editor_context_menu_plugin.h"
 #include "editor/themes/editor_scale.h"
 #include "editor/themes/editor_scale.h"
+#include "scene/gui/grid_container.h"
 #include "scene/gui/menu_button.h"
 #include "scene/gui/menu_button.h"
 #include "scene/gui/rich_text_label.h"
 #include "scene/gui/rich_text_label.h"
 #include "scene/gui/slider.h"
 #include "scene/gui/slider.h"
@@ -2845,7 +2846,7 @@ ScriptTextEditor::ScriptTextEditor() {
 	inline_color_options->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
 	inline_color_options->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
 	inline_color_options->set_fit_to_longest_item(false);
 	inline_color_options->set_fit_to_longest_item(false);
 	inline_color_options->connect("item_selected", callable_mp(this, &ScriptTextEditor::_update_color_text).unbind(1));
 	inline_color_options->connect("item_selected", callable_mp(this, &ScriptTextEditor::_update_color_text).unbind(1));
-	inline_color_picker->get_slider(ColorPicker::SLIDER_COUNT)->get_parent()->add_sibling(inline_color_options);
+	inline_color_picker->get_slider_container()->add_sibling(inline_color_options);
 
 
 	connection_info_dialog = memnew(ConnectionInfoDialog);
 	connection_info_dialog = memnew(ConnectionInfoDialog);
 
 

+ 60 - 25
scene/gui/color_picker.cpp

@@ -139,10 +139,10 @@ void ColorPicker::_notification(int p_what) {
 			}
 			}
 
 
 			if (current_shape != SHAPE_NONE) {
 			if (current_shape != SHAPE_NONE) {
-				btn_shape->set_button_icon(shape_popup->get_item_icon(current_shape));
+				btn_shape->set_button_icon(shape_popup->get_item_icon(get_current_shape_index()));
 			}
 			}
 
 
-			for (int i = 0; i < SLIDER_COUNT; i++) {
+			for (int i = 0; i < MODE_SLIDER_COUNT; i++) {
 				labels[i]->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
 				labels[i]->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
 				sliders[i]->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
 				sliders[i]->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
 			}
 			}
@@ -185,7 +185,7 @@ void ColorPicker::_notification(int p_what) {
 		case NOTIFICATION_FOCUS_ENTER:
 		case NOTIFICATION_FOCUS_ENTER:
 		case NOTIFICATION_FOCUS_EXIT: {
 		case NOTIFICATION_FOCUS_EXIT: {
 			if (current_shape != SHAPE_NONE) {
 			if (current_shape != SHAPE_NONE) {
-				shapes[current_shape]->cursor_editing = false;
+				shapes[get_current_shape_index()]->cursor_editing = false;
 			}
 			}
 		} break;
 		} break;
 
 
@@ -198,7 +198,7 @@ void ColorPicker::_notification(int p_what) {
 						input->is_action_just_released("ui_down")) {
 						input->is_action_just_released("ui_down")) {
 					gamepad_event_delay_ms = DEFAULT_GAMEPAD_EVENT_DELAY_MS;
 					gamepad_event_delay_ms = DEFAULT_GAMEPAD_EVENT_DELAY_MS;
 					if (current_shape == SHAPE_NONE) {
 					if (current_shape == SHAPE_NONE) {
-						shapes[current_shape]->echo_multiplier = 1;
+						shapes[get_current_shape_index()]->echo_multiplier = 1;
 					}
 					}
 					accept_event();
 					accept_event();
 					set_process_internal(false);
 					set_process_internal(false);
@@ -217,7 +217,7 @@ void ColorPicker::_notification(int p_what) {
 							input->is_action_pressed("ui_right") - input->is_action_pressed("ui_left"),
 							input->is_action_pressed("ui_right") - input->is_action_pressed("ui_left"),
 							input->is_action_pressed("ui_down") - input->is_action_pressed("ui_up"));
 							input->is_action_pressed("ui_down") - input->is_action_pressed("ui_up"));
 
 
-					shapes[current_shape]->update_cursor(color_change_vector, true);
+					shapes[get_current_shape_index()]->update_cursor(color_change_vector, true);
 					accept_event();
 					accept_event();
 				}
 				}
 				return;
 				return;
@@ -309,7 +309,7 @@ void fragment() {
 
 
 	circle_ok_color_shader.instantiate();
 	circle_ok_color_shader.instantiate();
 	circle_ok_color_shader->set_code(OK_COLOR_SHADER + R"(
 	circle_ok_color_shader->set_code(OK_COLOR_SHADER + R"(
-// ColorPicker ok color hsv circle shader.
+// ColorPicker ok color hsl circle shader.
 
 
 uniform float ok_hsl_l = 1.0;
 uniform float ok_hsl_l = 1.0;
 
 
@@ -330,12 +330,40 @@ void fragment() {
 	float b4 = float(sqrt(x * x + y * y) < 0.5);
 	float b4 = float(sqrt(x * x + y * y) < 0.5);
 	COLOR = vec4(col, (b + b2 + b3 + b4) / 4.00);
 	COLOR = vec4(col, (b + b2 + b3 + b4) / 4.00);
 })");
 })");
+
+	rectangle_ok_color_hs_shader.instantiate();
+	rectangle_ok_color_hs_shader->set_code(OK_COLOR_SHADER + R"(
+// ColorPicker ok color hs rectangle shader.
+
+uniform float ok_hsl_l = 0.0;
+
+void fragment() {
+	float h = UV.x;
+	float s = 1.0 - UV.y;
+	vec3 col = okhsl_to_srgb(vec3(h, s, ok_hsl_l));
+	COLOR = vec4(col, 1.0);
+})");
+
+	rectangle_ok_color_hl_shader.instantiate();
+	rectangle_ok_color_hl_shader->set_code(OK_COLOR_SHADER + R"(
+// ColorPicker ok color hl rectangle shader.
+
+uniform float ok_hsl_s = 0.0;
+
+void fragment() {
+	float h = UV.x;
+	float l = 1.0 - UV.y;
+	vec3 col = okhsl_to_srgb(vec3(h, ok_hsl_s, l));
+	COLOR = vec4(col, 1.0);
+})");
 }
 }
 
 
 void ColorPicker::finish_shaders() {
 void ColorPicker::finish_shaders() {
 	wheel_shader.unref();
 	wheel_shader.unref();
 	circle_shader.unref();
 	circle_shader.unref();
 	circle_ok_color_shader.unref();
 	circle_ok_color_shader.unref();
+	rectangle_ok_color_hs_shader.unref();
+	rectangle_ok_color_hl_shader.unref();
 }
 }
 
 
 void ColorPicker::set_focus_on_line_edit() {
 void ColorPicker::set_focus_on_line_edit() {
@@ -343,7 +371,7 @@ void ColorPicker::set_focus_on_line_edit() {
 }
 }
 
 
 void ColorPicker::set_focus_on_picker_shape() {
 void ColorPicker::set_focus_on_picker_shape() {
-	shapes[current_shape]->grab_focus();
+	shapes[get_current_shape_index()]->grab_focus();
 }
 }
 
 
 void ColorPicker::_update_controls() {
 void ColorPicker::_update_controls() {
@@ -384,7 +412,7 @@ void ColorPicker::_update_controls() {
 
 
 	int i = 0;
 	int i = 0;
 	for (ColorPickerShape *shape : shapes) {
 	for (ColorPickerShape *shape : shapes) {
-		bool is_active = current_shape == i;
+		bool is_active = get_current_shape_index() == i;
 		i++;
 		i++;
 
 
 		if (!shape->is_initialized) {
 		if (!shape->is_initialized) {
@@ -552,14 +580,14 @@ void ColorPicker::create_slider(GridContainer *gc, int idx) {
 	slider->connect("drag_started", callable_mp(this, &ColorPicker::_slider_drag_started));
 	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(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("drag_ended", callable_mp(this, &ColorPicker::_slider_drag_ended).unbind(1));
-	if (idx < SLIDER_COUNT) {
+	if (idx < MODE_SLIDER_COUNT) {
 		slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_slider_draw).bind(idx));
 		slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_slider_draw).bind(idx));
 	} else if (idx == SLIDER_ALPHA) {
 	} else if (idx == SLIDER_ALPHA) {
 		slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_alpha_slider_draw));
 		slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_alpha_slider_draw));
 	}
 	}
 	slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPicker::_slider_or_spin_input));
 	slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPicker::_slider_or_spin_input));
 
 
-	if (idx < SLIDER_COUNT) {
+	if (idx < MODE_SLIDER_COUNT) {
 		sliders[idx] = slider;
 		sliders[idx] = slider;
 		values[idx] = val;
 		values[idx] = val;
 		labels[idx] = lbl;
 		labels[idx] = lbl;
@@ -618,10 +646,8 @@ void ColorPicker::set_palette_saved_callback(const Callable &p_palette_saved) {
 #endif
 #endif
 
 
 HSlider *ColorPicker::get_slider(int p_idx) {
 HSlider *ColorPicker::get_slider(int p_idx) {
-	if (p_idx < SLIDER_COUNT) {
-		return sliders[p_idx];
-	}
-	return alpha_slider;
+	ERR_FAIL_INDEX_V(p_idx, MODE_MAX, nullptr);
+	return sliders[p_idx];
 }
 }
 
 
 Vector<float> ColorPicker::get_active_slider_values() {
 Vector<float> ColorPicker::get_active_slider_values() {
@@ -649,7 +675,7 @@ void ColorPicker::_copy_normalized_to_hsv_okhsl() {
 }
 }
 
 
 void ColorPicker::_copy_hsv_okhsl_to_normalized() {
 void ColorPicker::_copy_hsv_okhsl_to_normalized() {
-	if (current_shape != SHAPE_NONE && shapes[current_shape]->is_ok_hsl()) {
+	if (current_shape != SHAPE_NONE && shapes[get_current_shape_index()]->is_ok_hsl()) {
 		color_normalized.set_ok_hsl(ok_hsl_h, ok_hsl_s, ok_hsl_l, color_normalized.a);
 		color_normalized.set_ok_hsl(ok_hsl_h, ok_hsl_s, ok_hsl_l, color_normalized.a);
 	} else {
 	} else {
 		color_normalized.set_hsv(h, s, v, color_normalized.a);
 		color_normalized.set_hsv(h, s, v, color_normalized.a);
@@ -712,7 +738,7 @@ void ColorPicker::_reset_sliders_theme() {
 	style_box_flat->set_content_margin(SIDE_TOP, 16 * theme_cache.base_scale);
 	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());
 	style_box_flat->set_bg_color(Color(0.2, 0.23, 0.31).lerp(Color(0, 0, 0, 1), 0.3).clamp());
 
 
-	for (int i = 0; i < SLIDER_COUNT; i++) {
+	for (int i = 0; i < MODE_SLIDER_COUNT; i++) {
 		sliders[i]->begin_bulk_theme_override();
 		sliders[i]->begin_bulk_theme_override();
 		sliders[i]->add_theme_icon_override(SNAME("grabber"), theme_cache.bar_arrow);
 		sliders[i]->add_theme_icon_override(SNAME("grabber"), theme_cache.bar_arrow);
 		sliders[i]->add_theme_icon_override(SNAME("grabber_highlight"), theme_cache.bar_arrow);
 		sliders[i]->add_theme_icon_override(SNAME("grabber_highlight"), theme_cache.bar_arrow);
@@ -815,7 +841,7 @@ void ColorPicker::_update_color(bool p_update_sliders) {
 	_update_text_value();
 	_update_text_value();
 
 
 	if (current_shape != SHAPE_NONE) {
 	if (current_shape != SHAPE_NONE) {
-		for (Control *control : shapes[current_shape]->controls) {
+		for (Control *control : shapes[get_current_shape_index()]->controls) {
 			control->queue_redraw();
 			control->queue_redraw();
 		}
 		}
 	}
 	}
@@ -931,11 +957,11 @@ void ColorPicker::set_picker_shape(PickerShapeType p_shape) {
 		return;
 		return;
 	}
 	}
 	if (current_shape != SHAPE_NONE) {
 	if (current_shape != SHAPE_NONE) {
-		shape_popup->set_item_checked(current_shape, false);
+		shape_popup->set_item_checked(get_current_shape_index(), false);
 	}
 	}
 	if (p_shape != SHAPE_NONE) {
 	if (p_shape != SHAPE_NONE) {
-		shape_popup->set_item_checked(p_shape, true);
-		btn_shape->set_button_icon(shape_popup->get_item_icon(p_shape));
+		shape_popup->set_item_checked(shape_to_index(p_shape), true);
+		btn_shape->set_button_icon(shape_popup->get_item_icon(shape_to_index(p_shape)));
 	}
 	}
 
 
 	current_shape = p_shape;
 	current_shape = p_shape;
@@ -1018,6 +1044,11 @@ void ColorPicker::_quick_open_palette_file_selected(const String &p_path) {
 	file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
 	file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
 	_palette_file_selected(p_path);
 	_palette_file_selected(p_path);
 }
 }
+
+GridContainer *ColorPicker::get_slider_container() {
+	return slider_gc;
+}
+
 #endif // ifdef TOOLS_ENABLED
 #endif // ifdef TOOLS_ENABLED
 
 
 void ColorPicker::_palette_file_selected(const String &p_path) {
 void ColorPicker::_palette_file_selected(const String &p_path) {
@@ -1344,7 +1375,7 @@ void ColorPicker::set_colorize_sliders(bool p_colorize_sliders) {
 	if (colorize_sliders) {
 	if (colorize_sliders) {
 		Ref<StyleBoxEmpty> style_box_empty(memnew(StyleBoxEmpty));
 		Ref<StyleBoxEmpty> style_box_empty(memnew(StyleBoxEmpty));
 
 
-		for (int i = 0; i < SLIDER_COUNT; i++) {
+		for (int i = 0; i < MODE_SLIDER_COUNT; i++) {
 			sliders[i]->add_theme_style_override("slider", style_box_empty);
 			sliders[i]->add_theme_style_override("slider", style_box_empty);
 		}
 		}
 
 
@@ -1354,7 +1385,7 @@ void ColorPicker::set_colorize_sliders(bool p_colorize_sliders) {
 		style_box_flat->set_content_margin(SIDE_TOP, 16 * theme_cache.base_scale);
 		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());
 		style_box_flat->set_bg_color(Color(0.2, 0.23, 0.31).lerp(Color(0, 0, 0, 1), 0.3).clamp());
 
 
-		for (int i = 0; i < SLIDER_COUNT; i++) {
+		for (int i = 0; i < MODE_SLIDER_COUNT; i++) {
 			sliders[i]->add_theme_style_override("slider", style_box_flat);
 			sliders[i]->add_theme_style_override("slider", style_box_flat);
 		}
 		}
 
 
@@ -2051,7 +2082,7 @@ void ColorPicker::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_intensity"), "set_edit_intensity", "is_editing_intensity");
 	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::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::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::INT, "picker_shape", PROPERTY_HINT_ENUM, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle,OKHSL Circle,OK HS Rectangle:5,OK HL Rectangle,None:4"), "set_picker_shape", "get_picker_shape");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_add_swatches"), "set_can_add_swatches", "are_swatches_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_add_swatches"), "set_can_add_swatches", "are_swatches_enabled");
 	ADD_GROUP("Customization", "");
 	ADD_GROUP("Customization", "");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sampler_visible"), "set_sampler_visible", "is_sampler_visible");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sampler_visible"), "set_sampler_visible", "is_sampler_visible");
@@ -2077,6 +2108,8 @@ void ColorPicker::_bind_methods() {
 	BIND_ENUM_CONSTANT(SHAPE_VHS_CIRCLE);
 	BIND_ENUM_CONSTANT(SHAPE_VHS_CIRCLE);
 	BIND_ENUM_CONSTANT(SHAPE_OKHSL_CIRCLE);
 	BIND_ENUM_CONSTANT(SHAPE_OKHSL_CIRCLE);
 	BIND_ENUM_CONSTANT(SHAPE_NONE);
 	BIND_ENUM_CONSTANT(SHAPE_NONE);
+	BIND_ENUM_CONSTANT(SHAPE_OK_HS_RECTANGLE);
+	BIND_ENUM_CONSTANT(SHAPE_OK_HL_RECTANGLE);
 
 
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, ColorPicker, content_margin, "margin");
 	BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, ColorPicker, content_margin, "margin");
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, ColorPicker, label_width);
 	BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, ColorPicker, label_width);
@@ -2158,16 +2191,18 @@ ColorPicker::ColorPicker() {
 	add_shape(memnew(ColorPickerShapeWheel(this)));
 	add_shape(memnew(ColorPickerShapeWheel(this)));
 	add_shape(memnew(ColorPickerShapeVHSCircle(this)));
 	add_shape(memnew(ColorPickerShapeVHSCircle(this)));
 	add_shape(memnew(ColorPickerShapeOKHSLCircle(this)));
 	add_shape(memnew(ColorPickerShapeOKHSLCircle(this)));
+	add_shape(memnew(ColorPickerShapeOKHSRectangle(this)));
+	add_shape(memnew(ColorPickerShapeOKHLRectangle(this)));
 
 
 	shape_popup = btn_shape->get_popup();
 	shape_popup = btn_shape->get_popup();
 	{
 	{
 		int i = 0;
 		int i = 0;
 		for (const ColorPickerShape *shape : shapes) {
 		for (const ColorPickerShape *shape : shapes) {
-			shape_popup->add_radio_check_item(shape->get_name(), i);
+			shape_popup->add_radio_check_item(shape->get_name(), index_to_shape(i));
 			i++;
 			i++;
 		}
 		}
 	}
 	}
-	shape_popup->set_item_checked(current_shape, true);
+	shape_popup->set_item_checked(get_current_shape_index(), true);
 	shape_popup->connect(SceneStringName(id_pressed), callable_mp(this, &ColorPicker::set_picker_shape));
 	shape_popup->connect(SceneStringName(id_pressed), callable_mp(this, &ColorPicker::set_picker_shape));
 	shape_popup->connect("about_to_popup", callable_mp(this, &ColorPicker::_block_input_on_popup_show));
 	shape_popup->connect("about_to_popup", callable_mp(this, &ColorPicker::_block_input_on_popup_show));
 	shape_popup->connect(SNAME("popup_hide"), callable_mp(this, &ColorPicker::_enable_input_on_popup_hide));
 	shape_popup->connect(SNAME("popup_hide"), callable_mp(this, &ColorPicker::_enable_input_on_popup_hide));

+ 40 - 4
scene/gui/color_picker.h

@@ -88,6 +88,8 @@ class ColorPicker : public VBoxContainer {
 	friend class ColorPickerShapeCircle;
 	friend class ColorPickerShapeCircle;
 	friend class ColorPickerShapeVHSCircle;
 	friend class ColorPickerShapeVHSCircle;
 	friend class ColorPickerShapeOKHSLCircle;
 	friend class ColorPickerShapeOKHSLCircle;
+	friend class ColorPickerShapeOKHSRectangle;
+	friend class ColorPickerShapeOKHLRectangle;
 
 
 	friend class ColorModeRGB;
 	friend class ColorModeRGB;
 	friend class ColorModeHSV;
 	friend class ColorModeHSV;
@@ -113,19 +115,49 @@ public:
 		SHAPE_VHS_CIRCLE,
 		SHAPE_VHS_CIRCLE,
 		SHAPE_OKHSL_CIRCLE,
 		SHAPE_OKHSL_CIRCLE,
 		SHAPE_NONE,
 		SHAPE_NONE,
+		SHAPE_OK_HS_RECTANGLE,
+		SHAPE_OK_HL_RECTANGLE,
 
 
 		SHAPE_MAX
 		SHAPE_MAX
 	};
 	};
 
 
-	static const int SLIDER_COUNT = 3;
+private:
+	// Ideally, `SHAPE_NONE` should be -1 so that we don't need to convert shape type to index.
+	// In order to avoid breaking compatibility, we have to use these methods for conversion.
+	inline int get_current_shape_index() {
+		return shape_to_index(current_shape);
+	}
+
+	static inline int shape_to_index(PickerShapeType p_shape) {
+		if (p_shape == SHAPE_NONE) {
+			return -1;
+		}
+		if (p_shape > SHAPE_NONE) {
+			return p_shape - 1;
+		}
+		return p_shape;
+	}
+
+	static inline PickerShapeType index_to_shape(int p_index) {
+		if (p_index == -1) {
+			return SHAPE_NONE;
+		}
+		if (p_index >= SHAPE_NONE) {
+			return (PickerShapeType)(p_index + 1);
+		}
+		return (PickerShapeType)p_index;
+	}
+
+public:
+	static const int MODE_SLIDER_COUNT = 3;
+
 	enum SLIDER_EXTRA {
 	enum SLIDER_EXTRA {
-		SLIDER_INTENSITY = 3,
+		SLIDER_INTENSITY = MODE_SLIDER_COUNT,
 		SLIDER_ALPHA,
 		SLIDER_ALPHA,
 
 
 		SLIDER_MAX
 		SLIDER_MAX
 	};
 	};
 
 
-private:
 	enum class MenuOption {
 	enum class MenuOption {
 		MENU_SAVE,
 		MENU_SAVE,
 		MENU_SAVE_AS,
 		MENU_SAVE_AS,
@@ -134,9 +166,12 @@ private:
 		MENU_CLEAR,
 		MENU_CLEAR,
 	};
 	};
 
 
+private:
 	static inline Ref<Shader> wheel_shader;
 	static inline Ref<Shader> wheel_shader;
 	static inline Ref<Shader> circle_shader;
 	static inline Ref<Shader> circle_shader;
 	static inline Ref<Shader> circle_ok_color_shader;
 	static inline Ref<Shader> circle_ok_color_shader;
+	static inline Ref<Shader> rectangle_ok_color_hs_shader;
+	static inline Ref<Shader> rectangle_ok_color_hl_shader;
 	static inline List<Color> preset_cache;
 	static inline List<Color> preset_cache;
 	static inline List<Color> recent_preset_cache;
 	static inline List<Color> recent_preset_cache;
 
 
@@ -144,7 +179,7 @@ private:
 	Object *editor_settings = nullptr;
 	Object *editor_settings = nullptr;
 #endif
 #endif
 
 
-	int current_slider_count = SLIDER_COUNT;
+	int current_slider_count = MODE_SLIDER_COUNT;
 
 
 	const float DEFAULT_GAMEPAD_EVENT_DELAY_MS = 1.0 / 2;
 	const float DEFAULT_GAMEPAD_EVENT_DELAY_MS = 1.0 / 2;
 	const float GAMEPAD_EVENT_REPEAT_RATE_MS = 1.0 / 30;
 	const float GAMEPAD_EVENT_REPEAT_RATE_MS = 1.0 / 30;
@@ -394,6 +429,7 @@ public:
 	void _quick_open_palette_file_selected(const String &p_path);
 	void _quick_open_palette_file_selected(const String &p_path);
 #endif
 #endif
 
 
+	GridContainer *get_slider_container();
 	HSlider *get_slider(int idx);
 	HSlider *get_slider(int idx);
 	Vector<float> get_active_slider_values();
 	Vector<float> get_active_slider_values();
 
 

+ 220 - 6
scene/gui/color_picker_shape.cpp

@@ -131,7 +131,7 @@ void ColorPickerShape::draw_sv_square(Control *p_control, const Rect2 &p_square,
 		Color(1, 1, 1, 1),
 		Color(1, 1, 1, 1),
 		Color(1, 1, 1, 1),
 		Color(1, 1, 1, 1),
 		Color(0, 0, 0, 1),
 		Color(0, 0, 0, 1),
-		Color(0, 0, 0, 1)
+		Color(0, 0, 0, 1),
 	};
 	};
 	p_control->draw_polygon(points, colors);
 	p_control->draw_polygon(points, colors);
 
 
@@ -139,7 +139,7 @@ void ColorPickerShape::draw_sv_square(Control *p_control, const Rect2 &p_square,
 		Color(color1, 0),
 		Color(color1, 0),
 		Color(color1, 1),
 		Color(color1, 1),
 		Color(color2, 1),
 		Color(color2, 1),
-		Color(color2, 0)
+		Color(color2, 0),
 	};
 	};
 	p_control->draw_polygon(points, colors);
 	p_control->draw_polygon(points, colors);
 
 
@@ -356,6 +356,220 @@ void ColorPickerShapeRectangle::grab_focus() {
 	hue_slider->grab_focus();
 	hue_slider->grab_focus();
 }
 }
 
 
+void ColorPickerShapeOKHSRectangle::_initialize_controls() {
+	rectangle_margin = memnew(MarginContainer);
+	color_picker->shape_container->add_child(rectangle_margin);
+
+	Ref<ShaderMaterial> material;
+	material.instantiate();
+	material->set_shader(_get_shader());
+
+	square = memnew(Control);
+	rectangle_margin->add_child(square);
+	square->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeOKHSRectangle::_square_draw));
+	square->set_material(material);
+
+	square_overlay = memnew(Control);
+	rectangle_margin->add_child(square_overlay);
+	square_overlay->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+	square_overlay->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeOKHSRectangle::_square_overlay_input));
+	square_overlay->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeOKHSRectangle::_square_overlay_draw));
+	connect_shape_focus(square_overlay);
+
+	value_slider = memnew(Control);
+	color_picker->shape_container->add_child(value_slider);
+	value_slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeOKHSRectangle::_value_slider_input));
+	value_slider->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeOKHSRectangle::_value_slider_draw));
+	connect_shape_focus(value_slider);
+
+	controls.append(rectangle_margin);
+	controls.append(square);
+	controls.append(square_overlay);
+	controls.append(value_slider);
+}
+
+void ColorPickerShapeOKHSRectangle::update_theme() {
+	const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
+	rectangle_margin->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
+	value_slider->set_custom_minimum_size(Size2(theme_cache.h_width, 0));
+}
+
+void ColorPickerShapeOKHSRectangle::grab_focus() {
+	square_overlay->grab_focus();
+}
+
+void ColorPickerShapeOKHSRectangle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
+	if (square_overlay->has_focus()) {
+		color_picker->ok_hsl_h = CLAMP(color_picker->ok_hsl_h + p_color_change_vector.x / 100.0, 0, 1);
+		color_picker->ok_hsl_s = CLAMP(color_picker->ok_hsl_s - p_color_change_vector.y / 100.0, 0, 1);
+	} else if (value_slider->has_focus()) {
+		color_picker->ok_hsl_l = CLAMP(color_picker->ok_hsl_l + p_color_change_vector.y * echo_multiplier / 360.0, 0, 1);
+	}
+}
+
+void ColorPickerShapeOKHSRectangle::_square_draw() {
+	Ref<ShaderMaterial> material = square->get_material();
+	material->set_shader_parameter(SNAME("ok_hsl_l"), color_picker->ok_hsl_l);
+	square->draw_rect(Rect2(Point2(), square->get_size()), Color(1, 1, 1));
+}
+
+void ColorPickerShapeOKHSRectangle::_square_overlay_input(const Ref<InputEvent> &p_event) {
+	handle_cursor_editing(p_event, square_overlay);
+
+	Vector2 event_position;
+	if (!can_handle(p_event, event_position)) {
+		return;
+	}
+	event_position = (event_position / square_overlay->get_size()).clampf(0.0, 1.0);
+
+	color_picker->ok_hsl_h = event_position.x;
+	color_picker->ok_hsl_s = 1.0 - event_position.y;
+
+	apply_color();
+}
+
+void ColorPickerShapeOKHSRectangle::_square_overlay_draw() {
+	const Rect2 rect = Rect2(Vector2(), square_overlay->get_size());
+	const Vector2 end = rect.get_end();
+	Vector2 cursor_pos;
+	cursor_pos.x = CLAMP(rect.position.x + rect.size.x * color_picker->ok_hsl_h, rect.position.x, end.x);
+	cursor_pos.y = CLAMP(rect.position.y + rect.size.y * (1.0 - color_picker->ok_hsl_s), rect.position.y, end.y);
+
+	draw_focus_rect(square_overlay);
+	draw_cursor(square_overlay, cursor_pos);
+}
+
+void ColorPickerShapeOKHSRectangle::_value_slider_input(const Ref<InputEvent> &p_event) {
+	handle_cursor_editing(p_event, value_slider);
+
+	Vector2 event_position;
+	if (!can_handle(p_event, event_position)) {
+		return;
+	}
+	color_picker->ok_hsl_l = 1 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
+	apply_color();
+}
+
+void ColorPickerShapeOKHSRectangle::_value_slider_draw() {
+	const float ok_hsl_h = color_picker->ok_hsl_h;
+	const float ok_hsl_s = color_picker->ok_hsl_s;
+
+	const Vector2 size = value_slider->get_size();
+	PackedVector2Array points{
+		Vector2(size.x, 0),
+		Vector2(size.x, size.y * 0.5),
+		size,
+		Vector2(0, size.y),
+		Vector2(0, size.y * 0.5),
+		Vector2(),
+	};
+
+	Color color1 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 1);
+	Color color2 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0.5);
+	Color color3 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0);
+	PackedColorArray colors = {
+		color1,
+		color2,
+		color3,
+		color3,
+		color2,
+		color1,
+	};
+	value_slider->draw_polygon(points, colors);
+
+	draw_focus_rect(value_slider);
+
+	int y = size.y * (1 - CLAMP(color_picker->ok_hsl_l, 0, 1));
+	const Color color = Color::from_ok_hsl(ok_hsl_h, 1, color_picker->ok_hsl_l);
+	value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), color.inverted());
+}
+
+void ColorPickerShapeOKHLRectangle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
+	if (square_overlay->has_focus()) {
+		color_picker->ok_hsl_h = CLAMP(color_picker->ok_hsl_h + p_color_change_vector.x / 100.0, 0, 1);
+		color_picker->ok_hsl_l = CLAMP(color_picker->ok_hsl_l - p_color_change_vector.y / 100.0, 0, 1);
+	} else if (value_slider->has_focus()) {
+		color_picker->ok_hsl_s = CLAMP(color_picker->ok_hsl_s + p_color_change_vector.y * echo_multiplier / 360.0, 0, 1);
+	}
+}
+
+void ColorPickerShapeOKHLRectangle::_square_overlay_input(const Ref<InputEvent> &p_event) {
+	handle_cursor_editing(p_event, square_overlay);
+
+	Vector2 event_position;
+	if (!can_handle(p_event, event_position)) {
+		return;
+	}
+	event_position = (event_position / square_overlay->get_size()).clampf(0.0, 1.0);
+
+	color_picker->ok_hsl_h = event_position.x;
+	color_picker->ok_hsl_l = 1.0 - event_position.y;
+
+	apply_color();
+}
+
+void ColorPickerShapeOKHLRectangle::_square_overlay_draw() {
+	const Rect2 rect = Rect2(Vector2(), square_overlay->get_size());
+	const Vector2 end = rect.get_end();
+	Vector2 cursor_pos;
+	cursor_pos.x = CLAMP(rect.position.x + rect.size.x * color_picker->ok_hsl_h, rect.position.x, end.x);
+	cursor_pos.y = CLAMP(rect.position.y + rect.size.y * (1.0 - color_picker->ok_hsl_l), rect.position.y, end.y);
+
+	draw_focus_rect(square_overlay);
+	draw_cursor(square_overlay, cursor_pos);
+}
+
+void ColorPickerShapeOKHLRectangle::_square_draw() {
+	Ref<ShaderMaterial> material = square->get_material();
+	material->set_shader_parameter(SNAME("ok_hsl_s"), color_picker->ok_hsl_s);
+	square->draw_rect(Rect2(Point2(), square->get_size()), Color(1, 1, 1));
+}
+
+void ColorPickerShapeOKHLRectangle::_value_slider_input(const Ref<InputEvent> &p_event) {
+	handle_cursor_editing(p_event, value_slider);
+
+	Vector2 event_position;
+	if (!can_handle(p_event, event_position)) {
+		return;
+	}
+	color_picker->ok_hsl_s = 1 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
+	apply_color();
+}
+
+void ColorPickerShapeOKHLRectangle::_value_slider_draw() {
+	const float ok_hsl_h = color_picker->ok_hsl_h;
+	const float ok_hsl_l = color_picker->ok_hsl_l;
+
+	const Vector2 size = value_slider->get_size();
+	PackedVector2Array points{
+		Vector2(size.x, 0),
+		Vector2(size.x, size.y * 0.5),
+		size,
+		Vector2(0, size.y),
+		Vector2(0, size.y * 0.5),
+		Vector2(),
+	};
+
+	Color color1 = Color::from_ok_hsl(ok_hsl_h, 1, ok_hsl_l);
+	Color color2 = Color::from_ok_hsl(ok_hsl_h, 0.5, ok_hsl_l);
+	Color color3 = Color::from_ok_hsl(ok_hsl_h, 0, ok_hsl_l);
+	PackedColorArray colors = {
+		color1,
+		color2,
+		color3,
+		color3,
+		color2,
+		color1,
+	};
+	value_slider->draw_polygon(points, colors);
+
+	draw_focus_rect(value_slider);
+
+	int y = size.y * (1 - CLAMP(color_picker->ok_hsl_s, 0, 1));
+	const Color color = Color::from_ok_hsl(ok_hsl_h, 1, ok_hsl_l);
+	value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), color.inverted());
+}
+
 float ColorPickerShapeWheel::_get_h_on_wheel(const Vector2 &p_color_change_vector) {
 float ColorPickerShapeWheel::_get_h_on_wheel(const Vector2 &p_color_change_vector) {
 	int h_change = get_edge_h_change(p_color_change_vector);
 	int h_change = get_edge_h_change(p_color_change_vector);
 
 
@@ -645,7 +859,7 @@ void ColorPickerShapeVHSCircle::_value_slider_draw() {
 		Vector2(),
 		Vector2(),
 		Vector2(size.x, 0),
 		Vector2(size.x, 0),
 		size,
 		size,
-		Vector2(0, size.y)
+		Vector2(0, size.y),
 	};
 	};
 
 
 	Color color = Color::from_hsv(color_picker->h, color_picker->s, 1);
 	Color color = Color::from_hsv(color_picker->h, color_picker->s, 1);
@@ -653,7 +867,7 @@ void ColorPickerShapeVHSCircle::_value_slider_draw() {
 		color,
 		color,
 		color,
 		color,
 		Color(),
 		Color(),
-		Color()
+		Color(),
 	};
 	};
 
 
 	value_slider->draw_polygon(points, colors);
 	value_slider->draw_polygon(points, colors);
@@ -736,7 +950,7 @@ void ColorPickerShapeOKHSLCircle::_value_slider_draw() {
 		size,
 		size,
 		Vector2(0, size.y),
 		Vector2(0, size.y),
 		Vector2(0, size.y * 0.5),
 		Vector2(0, size.y * 0.5),
-		Vector2()
+		Vector2(),
 	};
 	};
 
 
 	Color color1 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 1);
 	Color color1 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 1);
@@ -755,7 +969,7 @@ void ColorPickerShapeOKHSLCircle::_value_slider_draw() {
 	draw_focus_rect(value_slider);
 	draw_focus_rect(value_slider);
 
 
 	int y = size.y * (1 - CLAMP(ok_hsl_l, 0, 1));
 	int y = size.y * (1 - CLAMP(ok_hsl_l, 0, 1));
-	value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), Color::from_hsv(ok_hsl_h, 1, ok_hsl_l).inverted());
+	value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), Color::from_ok_hsl(ok_hsl_h, 1, ok_hsl_l).inverted());
 }
 }
 
 
 void ColorPickerShapeOKHSLCircle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
 void ColorPickerShapeOKHSLCircle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {

+ 54 - 0
scene/gui/color_picker_shape.h

@@ -104,6 +104,60 @@ public:
 			ColorPickerShape(p_color_picker) {}
 			ColorPickerShape(p_color_picker) {}
 };
 };
 
 
+class ColorPickerShapeOKHSRectangle : public ColorPickerShape {
+	GDCLASS(ColorPickerShapeOKHSRectangle, ColorPickerShape);
+
+	MarginContainer *rectangle_margin = nullptr;
+
+protected:
+	Control *square = nullptr;
+	Control *square_overlay = nullptr;
+	Control *value_slider = nullptr;
+	virtual Ref<Shader> _get_shader() const { return ColorPicker::rectangle_ok_color_hs_shader; }
+	virtual void _initialize_controls() override;
+	virtual void _update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) override;
+
+	virtual void _square_draw();
+	virtual void _square_overlay_input(const Ref<InputEvent> &p_event);
+	virtual void _square_overlay_draw();
+
+	virtual void _value_slider_input(const Ref<InputEvent> &p_event);
+	virtual void _value_slider_draw();
+
+public:
+	virtual String get_name() const override { return ETR("OK HS Rectangle"); }
+	virtual bool is_ok_hsl() const override { return true; }
+	virtual Ref<Texture2D> get_icon() const override { return color_picker->theme_cache.shape_rect; }
+	virtual void update_theme() override;
+	virtual void grab_focus() override;
+
+	ColorPickerShapeOKHSRectangle(ColorPicker *p_color_picker) :
+			ColorPickerShape(p_color_picker) {}
+};
+
+class ColorPickerShapeOKHLRectangle : public ColorPickerShapeOKHSRectangle {
+	GDCLASS(ColorPickerShapeOKHLRectangle, ColorPickerShapeOKHSRectangle);
+
+protected:
+	virtual Ref<Shader> _get_shader() const override { return ColorPicker::rectangle_ok_color_hl_shader; }
+	virtual void _update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) override;
+
+	virtual void _square_draw() override;
+	virtual void _square_overlay_input(const Ref<InputEvent> &p_event) override;
+	virtual void _square_overlay_draw() override;
+
+	virtual void _value_slider_input(const Ref<InputEvent> &p_event) override;
+	virtual void _value_slider_draw() override;
+
+public:
+	virtual String get_name() const override { return ETR("OK HL Rectangle"); }
+	virtual bool is_ok_hsl() const override { return true; }
+	virtual Ref<Texture2D> get_icon() const override { return color_picker->theme_cache.shape_rect; }
+
+	ColorPickerShapeOKHLRectangle(ColorPicker *p_color_picker) :
+			ColorPickerShapeOKHSRectangle(p_color_picker) {}
+};
+
 class ColorPickerShapeWheel : public ColorPickerShape {
 class ColorPickerShapeWheel : public ColorPickerShape {
 	GDCLASS(ColorPickerShapeWheel, ColorPickerShape);
 	GDCLASS(ColorPickerShapeWheel, ColorPickerShape);