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

ColorPicker: Add an intensity slider to all modes for HDR
This commit is contained in:
Thaddeus Crews
2025-05-27 09:39:21 -05:00
13 changed files with 545 additions and 298 deletions

View File

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

View File

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

View File

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

View File

@ -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));

View File

@ -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, "")

View File

@ -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)));

View File

@ -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);
left_color = color;
left_color.a = 0;
right_color = color;
right_color.a = 1;
} else {
left_color = Color(
p_which == 0 ? 0 : color.r,
p_which == 1 ? 0 : color.g,
p_which == 2 ? 0 : color.b);
right_color = Color(
p_which == 0 ? 1 : color.r,
p_which == 1 ? 1 : color.g,
p_which == 2 ? 1 : color.b);
if (rgb_texture[p_which].is_null()) {
rgb_texture[p_which].instantiate();
rgb_texture[p_which]->set_width(400);
rgb_texture[p_which]->set_height(6);
}
col.set(0, left_color);
col.set(1, right_color);
col.set(2, right_color);
col.set(3, left_color);
pos.set(0, Vector2(0, 0));
pos.set(1, Vector2(size.x, 0));
pos.set(2, Vector2(size.x, margin));
pos.set(3, Vector2(0, margin));
Ref<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_SRGB);
gradient_texture->set_gradient(gradient);
}
PackedColorArray colors = { left_color, right_color };
gradient->set_colors(colors);
slider->draw_texture_rect(gradient_texture, Rect2(Vector2(), Vector2(size.x, margin)), false);
}
void ColorModeHSV::_value_changed() {
@ -108,38 +129,43 @@ void ColorModeHSV::_value_changed() {
if (values[2] > 0 || values[1] != cached_saturation) {
cached_saturation = values[1];
}
// Cache real HSV values in ColorPicker.
color_picker->h = color_picker->sliders[0]->get_value() / 360.0;
color_picker->s = color_picker->sliders[1]->get_value() / 100.0;
color_picker->v = color_picker->sliders[2]->get_value() / 100.0;
color_picker->hsv_cached = true;
}
String ColorModeHSV::get_slider_label(int idx) const {
ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label.");
ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), String(), "Couldn't get slider label.");
return labels[idx];
}
float ColorModeHSV::get_slider_max(int idx) const {
ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value.");
ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), 0, "Couldn't get slider max value.");
return slider_max[idx];
}
float ColorModeHSV::get_slider_value(int idx) const {
switch (idx) {
case 0: {
if (color_picker->get_pick_color().get_s() > 0) {
return color_picker->get_pick_color().get_h() * 360.0;
if (color_picker->color_normalized.get_s() > 0) {
return color_picker->color_normalized.get_h() * 360.0;
} else {
return cached_hue;
}
}
case 1: {
if (color_picker->get_pick_color().get_v() > 0) {
return color_picker->get_pick_color().get_s() * 100.0;
if (color_picker->color_normalized.get_v() > 0) {
return color_picker->color_normalized.get_s() * 100.0;
} else {
return cached_saturation;
}
}
case 2:
return color_picker->get_pick_color().get_v() * 100.0;
case 3:
return Math::round(color_picker->get_pick_color().components[3] * 255.0);
return color_picker->color_normalized.get_v() * 100.0;
default:
ERR_FAIL_V_MSG(0, "Couldn't get slider value.");
}
@ -147,8 +173,7 @@ float ColorModeHSV::get_slider_value(int idx) const {
Color ColorModeHSV::get_color() const {
Vector<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];
}
Color ColorModeRAW::get_color() const {
float ColorModeLinear::get_alpha_slider_max() const {
return 1;
}
float ColorModeLinear::get_alpha_slider_value() const {
return color_picker->get_pick_color().a;
}
Color ColorModeLinear::get_color() const {
Vector<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(
p_which == 0 ? 0 : color.r,
p_which == 1 ? 0 : color.g,
p_which == 2 ? 0 : color.b);
right_color = Color(
p_which == 0 ? 1 : color.r,
p_which == 1 ? 1 : color.g,
p_which == 2 ? 1 : color.b);
left_color = color;
left_color.a = 0;
right_color = color;
right_color.a = 1;
col.set(0, left_color);
col.set(1, right_color);
col.set(2, right_color);
col.set(3, left_color);
pos.set(0, Vector2(0, 0));
pos.set(1, Vector2(size.x, 0));
pos.set(2, Vector2(size.x, margin));
pos.set(3, Vector2(0, margin));
slider->draw_polygon(pos, col);
}
}
bool ColorModeRAW::apply_theme() const {
for (int i = 0; i < ColorPicker::SLIDER_COUNT; i++) {
HSlider *slider = color_picker->get_slider(i);
slider->remove_theme_icon_override("grabber");
slider->remove_theme_icon_override("grabber_highlight");
slider->remove_theme_style_override("slider");
slider->remove_theme_constant_override("grabber_offset");
if (rgb_texture[p_which].is_null()) {
rgb_texture[p_which].instantiate();
rgb_texture[p_which]->set_width(400);
rgb_texture[p_which]->set_height(6);
}
return true;
Ref<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_LINEAR_SRGB);
gradient_texture->set_gradient(gradient);
}
PackedColorArray colors = { left_color, right_color };
gradient->set_colors(colors);
slider->draw_texture_rect(gradient_texture, Rect2(Vector2(), Vector2(size.x, margin)), false);
}
void ColorModeOKHSL::_value_changed() {
@ -281,38 +327,43 @@ void ColorModeOKHSL::_value_changed() {
if (values[2] > 0 || values[1] != cached_saturation) {
cached_saturation = values[1];
}
// Cache real OKHSL values in ColorPicker.
color_picker->ok_hsl_h = color_picker->sliders[0]->get_value() / 360.0;
color_picker->ok_hsl_s = color_picker->sliders[1]->get_value() / 100.0;
color_picker->ok_hsl_l = color_picker->sliders[2]->get_value() / 100.0;
color_picker->okhsl_cached = true;
}
String ColorModeOKHSL::get_slider_label(int idx) const {
ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label.");
ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), String(), "Couldn't get slider label.");
return labels[idx];
}
float ColorModeOKHSL::get_slider_max(int idx) const {
ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value.");
ERR_FAIL_INDEX_V_MSG(idx, get_slider_count(), 0, "Couldn't get slider max value.");
return slider_max[idx];
}
float ColorModeOKHSL::get_slider_value(int idx) const {
switch (idx) {
case 0: {
if (color_picker->get_pick_color().get_ok_hsl_s() > 0) {
return color_picker->get_pick_color().get_ok_hsl_h() * 360.0;
if (color_picker->color_normalized.get_ok_hsl_s() > 0) {
return color_picker->color_normalized.get_ok_hsl_h() * 360.0;
} else {
return cached_hue;
}
}
case 1: {
if (color_picker->get_pick_color().get_ok_hsl_l() > 0) {
return color_picker->get_pick_color().get_ok_hsl_s() * 100.0;
if (color_picker->color_normalized.get_ok_hsl_l() > 0) {
return color_picker->color_normalized.get_ok_hsl_s() * 100.0;
} else {
return cached_saturation;
}
}
case 2:
return color_picker->get_pick_color().get_ok_hsl_l() * 100.0;
case 3:
return Math::round(color_picker->get_pick_color().components[3] * 255.0);
return color_picker->color_normalized.get_ok_hsl_l() * 100.0;
default:
ERR_FAIL_V_MSG(0, "Couldn't get slider value.");
}
@ -320,8 +371,7 @@ float ColorModeOKHSL::get_slider_value(int idx) const {
Color ColorModeOKHSL::get_color() const {
Vector<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);
}
}

View File

@ -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 slider_draw(int p_which) override;
virtual bool apply_theme() const override;
virtual void _greater_value_inputted() override;
ColorModeRAW(ColorPicker *p_color_picker) :
virtual void slider_draw(int p_which) override;
ColorModeLinear(ColorPicker *p_color_picker) :
ColorMode(p_color_picker) {}
};
class ColorModeOKHSL : public ColorMode {
public:
String labels[3] = { "H", "S", "L" };
float slider_max[4] = { 359, 100, 100, 255 };
float slider_max[3] = { 359, 100, 100 };
float cached_hue = 0.0;
float cached_saturation = 0.0;
Ref<GradientTexture2D> hue_texture;

View File

@ -31,6 +31,7 @@
#include "color_picker.h"
#include "core/io/image.h"
#include "core/math/expression.h"
#include "scene/gui/color_mode.h"
#include "scene/gui/color_picker_shape.h"
#include "scene/gui/file_dialog.h"
@ -53,6 +54,27 @@
#include "scene/theme/theme_db.h"
#include "thirdparty/misc/ok_color_shader.h"
static inline bool is_color_overbright(const Color &color) {
return (color.r > 1.0) || (color.g > 1.0) || (color.b > 1.0);
}
static inline bool is_color_valid_hex(const Color &color) {
return !is_color_overbright(color) && color.r >= 0 && color.g >= 0 && color.b >= 0;
}
static inline String color_to_string(const Color &color, bool show_alpha = true, bool force_value_format = false) {
if (!force_value_format && !is_color_overbright(color)) {
return "#" + color.to_html(show_alpha);
}
String t = "(" + String::num(color.r, 3) + ", " + String::num(color.g, 3) + ", " + String::num(color.b, 3);
if (show_alpha) {
t += ", " + String::num(color.a, 3) + ")";
} else {
t += ")";
}
return t;
}
void ColorPicker::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
@ -126,6 +148,7 @@ void ColorPicker::_notification(int p_what) {
}
alpha_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
alpha_slider->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers);
intensity_label->set_custom_minimum_size(Size2(theme_cache.label_width, 0));
for (int i = 0; i < MODE_BUTTON_COUNT; i++) {
mode_btns[i]->begin_bulk_theme_override();
@ -144,10 +167,9 @@ void ColorPicker::_notification(int p_what) {
_reset_sliders_theme();
if (Engine::get_singleton()->is_editor_hint()) {
// Adjust for the width of the "Script" icon.
text_type->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
}
hex_label->set_custom_minimum_size(Size2(38 * theme_cache.base_scale, 0));
// Adjust for the width of the "script" icon.
text_type->set_custom_minimum_size(Size2(28 * theme_cache.base_scale, 0));
_update_presets();
_update_recent_presets();
@ -348,17 +370,17 @@ void ColorPicker::_update_controls() {
alpha_slider->set_accessibility_name(ETR("Alpha"));
alpha_value->set_accessibility_name(ETR("Alpha"));
slider_theme_modified = modes[current_mode]->apply_theme();
intensity_label->set_text("I");
intensity_slider->set_accessibility_name(ETR("Intensity"));
intensity_value->set_accessibility_name(ETR("Intensity"));
if (edit_alpha) {
alpha_value->show();
alpha_slider->show();
alpha_label->show();
} else {
alpha_value->hide();
alpha_slider->hide();
alpha_label->hide();
}
alpha_value->set_visible(edit_alpha);
alpha_slider->set_visible(edit_alpha);
alpha_label->set_visible(edit_alpha);
intensity_value->set_visible(edit_intensity);
intensity_slider->set_visible(edit_intensity);
intensity_label->set_visible(edit_intensity);
int i = 0;
for (ColorPickerShape *shape : shapes) {
@ -381,17 +403,17 @@ void ColorPicker::_update_controls() {
btn_shape->set_visible(current_shape != SHAPE_NONE);
}
void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) {
void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders, bool p_calc_intensity) {
if (text_changed) {
add_recent_preset(color);
text_changed = false;
}
color = p_color;
if (color != last_color) {
_copy_color_to_hsv();
last_color = color;
if (p_calc_intensity) {
_copy_color_to_normalized_and_intensity();
}
_copy_normalized_to_hsv_okhsl();
if (!is_inside_tree()) {
return;
@ -401,7 +423,7 @@ void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) {
}
void ColorPicker::set_pick_color(const Color &p_color) {
_set_pick_color(p_color, true); //because setters can't have more arguments
_set_pick_color(p_color, true, true); // Because setters can't have more arguments.
}
void ColorPicker::set_old_color(const Color &p_color) {
@ -435,6 +457,32 @@ bool ColorPicker::is_editing_alpha() const {
return edit_alpha;
}
void ColorPicker::set_edit_intensity(bool p_show) {
if (edit_intensity == p_show) {
return;
}
if (p_show) {
set_pick_color(color);
} else {
_normalized_apply_intensity_to_color();
color_normalized = color;
intensity = 0;
}
edit_intensity = p_show;
_update_controls();
if (!is_inside_tree()) {
return;
}
_update_color();
sample->queue_redraw();
}
bool ColorPicker::is_editing_intensity() const {
return edit_intensity;
}
void ColorPicker::_slider_drag_started() {
currently_dragging = true;
}
@ -444,32 +492,18 @@ void ColorPicker::_slider_value_changed() {
return;
}
color = modes[current_mode]->get_color();
intensity = intensity_value->get_value();
color_normalized = modes[current_mode]->get_color();
if (edit_intensity && is_color_overbright(color_normalized)) {
modes[current_mode]->_greater_value_inputted();
color_normalized = modes[current_mode]->get_color();
}
_normalized_apply_intensity_to_color();
intensity_value->set_prefix(intensity < 0 ? "" : "+");
modes[current_mode]->_value_changed();
if (current_mode == MODE_HSV) {
h = sliders[0]->get_value() / 360.0;
s = sliders[1]->get_value() / 100.0;
v = sliders[2]->get_value() / 100.0;
ok_hsl_h = color.get_ok_hsl_h();
ok_hsl_s = color.get_ok_hsl_s();
ok_hsl_l = color.get_ok_hsl_l();
circle_keyboard_joypad_picker_cursor_position = Vector2i();
last_color = color;
} else if (current_mode == MODE_OKHSL) {
ok_hsl_h = sliders[0]->get_value() / 360.0;
ok_hsl_s = sliders[1]->get_value() / 100.0;
ok_hsl_l = sliders[2]->get_value() / 100.0;
h = color.get_h();
s = color.get_s();
v = color.get_v();
circle_keyboard_joypad_picker_cursor_position = Vector2i();
last_color = color;
}
_set_pick_color(color, false);
_set_pick_color(color, false, false);
if (!deferred_mode_enabled || !currently_dragging) {
emit_signal(SNAME("color_changed"), color);
}
@ -518,14 +552,22 @@ void ColorPicker::create_slider(GridContainer *gc, int idx) {
slider->connect("drag_started", callable_mp(this, &ColorPicker::_slider_drag_started));
slider->connect(SceneStringName(value_changed), callable_mp(this, &ColorPicker::_slider_value_changed).unbind(1));
slider->connect("drag_ended", callable_mp(this, &ColorPicker::_slider_drag_ended).unbind(1));
slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_slider_draw).bind(idx));
if (idx < SLIDER_COUNT) {
slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_slider_draw).bind(idx));
} else if (idx == SLIDER_ALPHA) {
slider->connect(SceneStringName(draw), callable_mp(this, &ColorPicker::_alpha_slider_draw));
}
slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPicker::_slider_or_spin_input));
if (idx < SLIDER_COUNT) {
sliders[idx] = slider;
values[idx] = val;
labels[idx] = lbl;
} else {
} else if (idx == SLIDER_INTENSITY) {
intensity_slider = slider;
intensity_value = val;
intensity_label = lbl;
} else if (idx == SLIDER_ALPHA) {
alpha_slider = slider;
alpha_value = val;
alpha_label = lbl;
@ -591,23 +633,55 @@ Vector<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) {
if (preset_group->get_pressed_button()) {
preset_group->get_pressed_button()->set_pressed(false);
@ -660,35 +734,52 @@ void ColorPicker::_reset_sliders_theme() {
}
void ColorPicker::_html_submitted(const String &p_html) {
if (updating || text_is_constructor || !c_text->is_visible()) {
if (updating) {
return;
}
Color new_color = Color::from_string(p_html.strip_edges(), color);
String html_no_prefix = p_html.strip_edges().trim_prefix("#");
if (html_no_prefix.is_valid_hex_number(false)) {
// Convert invalid HTML color codes that software like Figma supports.
if (html_no_prefix.length() == 1) {
// Turn `#1` into `#111111`.
html_no_prefix = html_no_prefix.repeat(6);
} else if (html_no_prefix.length() == 2) {
// Turn `#12` into `#121212`.
html_no_prefix = html_no_prefix.repeat(3);
} else if (html_no_prefix.length() == 5) {
// Turn `#12345` into `#11223344`.
html_no_prefix = html_no_prefix.left(4);
} else if (html_no_prefix.length() == 7) {
// Turn `#1234567` into `#123456`.
html_no_prefix = html_no_prefix.left(6);
Color new_color = color;
if (text_is_constructor || !is_color_valid_hex(color)) {
Ref<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);
}

View File

@ -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();

View File

@ -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),

View File

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

View File

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

After

Width:  |  Height:  |  Size: 305 B