From 7b4c95e6d82e4b86f137e56aaa8a2063e2556c0d Mon Sep 17 00:00:00 2001 From: LuoZhihao Date: Sat, 7 Jun 2025 22:30:40 +0800 Subject: [PATCH] ColorPicker: Add okhsl HS and HL rectangular picker shapes --- doc/classes/ColorPicker.xml | 6 + editor/editor_settings.cpp | 2 +- editor/plugins/script_text_editor.cpp | 3 +- scene/gui/color_picker.cpp | 85 +++++++--- scene/gui/color_picker.h | 44 ++++- scene/gui/color_picker_shape.cpp | 226 +++++++++++++++++++++++++- scene/gui/color_picker_shape.h | 54 ++++++ 7 files changed, 383 insertions(+), 37 deletions(-) diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index 07e17b066c..1ca6703c92 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -144,6 +144,12 @@ The color space shape and the shape select button are hidden. Can't be selected from the shapes popup. + + OKHSL Color Model rectangle with constant lightness. + + + OKHSL Color Model rectangle with constant saturation. + diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 7a97dc5902..fb009eb497 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -569,7 +569,7 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { _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_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, ""); // Theme diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 559c4ce73b..9c6a05d536 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -43,6 +43,7 @@ #include "editor/gui/editor_toaster.h" #include "editor/plugins/editor_context_menu_plugin.h" #include "editor/themes/editor_scale.h" +#include "scene/gui/grid_container.h" #include "scene/gui/menu_button.h" #include "scene/gui/rich_text_label.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_fit_to_longest_item(false); 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); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 0096d4d0fe..bbd543b8e8 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -139,10 +139,10 @@ void ColorPicker::_notification(int p_what) { } 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)); 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_EXIT: { if (current_shape != SHAPE_NONE) { - shapes[current_shape]->cursor_editing = false; + shapes[get_current_shape_index()]->cursor_editing = false; } } break; @@ -198,7 +198,7 @@ void ColorPicker::_notification(int p_what) { input->is_action_just_released("ui_down")) { gamepad_event_delay_ms = DEFAULT_GAMEPAD_EVENT_DELAY_MS; if (current_shape == SHAPE_NONE) { - shapes[current_shape]->echo_multiplier = 1; + shapes[get_current_shape_index()]->echo_multiplier = 1; } accept_event(); 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_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(); } return; @@ -309,7 +309,7 @@ void fragment() { circle_ok_color_shader.instantiate(); 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; @@ -330,12 +330,40 @@ void fragment() { float b4 = float(sqrt(x * x + y * y) < 0.5); 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() { wheel_shader.unref(); circle_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() { @@ -343,7 +371,7 @@ void ColorPicker::set_focus_on_line_edit() { } void ColorPicker::set_focus_on_picker_shape() { - shapes[current_shape]->grab_focus(); + shapes[get_current_shape_index()]->grab_focus(); } void ColorPicker::_update_controls() { @@ -384,7 +412,7 @@ void ColorPicker::_update_controls() { int i = 0; for (ColorPickerShape *shape : shapes) { - bool is_active = current_shape == i; + bool is_active = get_current_shape_index() == i; i++; 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(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)); - if (idx < SLIDER_COUNT) { + if (idx < MODE_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) { + if (idx < MODE_SLIDER_COUNT) { sliders[idx] = slider; values[idx] = val; labels[idx] = lbl; @@ -618,10 +646,8 @@ void ColorPicker::set_palette_saved_callback(const Callable &p_palette_saved) { #endif 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 ColorPicker::get_active_slider_values() { @@ -649,7 +675,7 @@ void ColorPicker::_copy_normalized_to_hsv_okhsl() { } 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); } else { 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_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]->add_theme_icon_override(SNAME("grabber"), 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(); if (current_shape != SHAPE_NONE) { - for (Control *control : shapes[current_shape]->controls) { + for (Control *control : shapes[get_current_shape_index()]->controls) { control->queue_redraw(); } } @@ -931,11 +957,11 @@ void ColorPicker::set_picker_shape(PickerShapeType p_shape) { return; } 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) { - 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; @@ -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); _palette_file_selected(p_path); } + +GridContainer *ColorPicker::get_slider_container() { + return slider_gc; +} + #endif // ifdef TOOLS_ENABLED 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) { Ref 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); } @@ -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_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); } @@ -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::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::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_GROUP("Customization", ""); 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_OKHSL_CIRCLE); 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(Theme::DATA_TYPE_CONSTANT, ColorPicker, label_width); @@ -2158,16 +2191,18 @@ ColorPicker::ColorPicker() { add_shape(memnew(ColorPickerShapeWheel(this))); add_shape(memnew(ColorPickerShapeVHSCircle(this))); add_shape(memnew(ColorPickerShapeOKHSLCircle(this))); + add_shape(memnew(ColorPickerShapeOKHSRectangle(this))); + add_shape(memnew(ColorPickerShapeOKHLRectangle(this))); shape_popup = btn_shape->get_popup(); { int i = 0; 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++; } } - 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("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)); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 9a1f212abe..26045f73db 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -88,6 +88,8 @@ class ColorPicker : public VBoxContainer { friend class ColorPickerShapeCircle; friend class ColorPickerShapeVHSCircle; friend class ColorPickerShapeOKHSLCircle; + friend class ColorPickerShapeOKHSRectangle; + friend class ColorPickerShapeOKHLRectangle; friend class ColorModeRGB; friend class ColorModeHSV; @@ -113,19 +115,49 @@ public: SHAPE_VHS_CIRCLE, SHAPE_OKHSL_CIRCLE, SHAPE_NONE, + SHAPE_OK_HS_RECTANGLE, + SHAPE_OK_HL_RECTANGLE, 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 { - SLIDER_INTENSITY = 3, + SLIDER_INTENSITY = MODE_SLIDER_COUNT, SLIDER_ALPHA, SLIDER_MAX }; -private: enum class MenuOption { MENU_SAVE, MENU_SAVE_AS, @@ -134,9 +166,12 @@ private: MENU_CLEAR, }; +private: static inline Ref wheel_shader; static inline Ref circle_shader; static inline Ref circle_ok_color_shader; + static inline Ref rectangle_ok_color_hs_shader; + static inline Ref rectangle_ok_color_hl_shader; static inline List preset_cache; static inline List recent_preset_cache; @@ -144,7 +179,7 @@ private: Object *editor_settings = nullptr; #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 GAMEPAD_EVENT_REPEAT_RATE_MS = 1.0 / 30; @@ -394,6 +429,7 @@ public: void _quick_open_palette_file_selected(const String &p_path); #endif + GridContainer *get_slider_container(); HSlider *get_slider(int idx); Vector get_active_slider_values(); diff --git a/scene/gui/color_picker_shape.cpp b/scene/gui/color_picker_shape.cpp index 23aebdc107..ce9de12a29 100644 --- a/scene/gui/color_picker_shape.cpp +++ b/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(0, 0, 0, 1), - Color(0, 0, 0, 1) + Color(0, 0, 0, 1), }; 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, 1), Color(color2, 1), - Color(color2, 0) + Color(color2, 0), }; p_control->draw_polygon(points, colors); @@ -356,6 +356,220 @@ void ColorPickerShapeRectangle::grab_focus() { hue_slider->grab_focus(); } +void ColorPickerShapeOKHSRectangle::_initialize_controls() { + rectangle_margin = memnew(MarginContainer); + color_picker->shape_container->add_child(rectangle_margin); + + Ref 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 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 &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 &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 &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 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 &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) { int h_change = get_edge_h_change(p_color_change_vector); @@ -645,7 +859,7 @@ void ColorPickerShapeVHSCircle::_value_slider_draw() { Vector2(), Vector2(size.x, 0), size, - Vector2(0, size.y) + Vector2(0, size.y), }; 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(), }; value_slider->draw_polygon(points, colors); @@ -736,7 +950,7 @@ void ColorPickerShapeOKHSLCircle::_value_slider_draw() { size, Vector2(0, size.y), Vector2(0, size.y * 0.5), - Vector2() + Vector2(), }; 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); 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) { diff --git a/scene/gui/color_picker_shape.h b/scene/gui/color_picker_shape.h index 6f8ac6a5dd..27418fb12c 100644 --- a/scene/gui/color_picker_shape.h +++ b/scene/gui/color_picker_shape.h @@ -104,6 +104,60 @@ public: 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 _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 &p_event); + virtual void _square_overlay_draw(); + + virtual void _value_slider_input(const Ref &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 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 _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 &p_event) override; + virtual void _square_overlay_draw() override; + + virtual void _value_slider_input(const Ref &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 get_icon() const override { return color_picker->theme_cache.shape_rect; } + + ColorPickerShapeOKHLRectangle(ColorPicker *p_color_picker) : + ColorPickerShapeOKHSRectangle(p_color_picker) {} +}; + class ColorPickerShapeWheel : public ColorPickerShape { GDCLASS(ColorPickerShapeWheel, ColorPickerShape);