summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/classes/ColorPicker.xml2
-rw-r--r--doc/classes/ItemList.xml3
-rw-r--r--editor/doc_tools.cpp57
-rw-r--r--editor/editor_file_dialog.cpp2
-rw-r--r--editor/editor_themes.cpp9
-rw-r--r--scene/gui/color_picker.cpp198
-rw-r--r--scene/gui/color_picker.h39
-rw-r--r--scene/gui/graph_edit.cpp16
-rw-r--r--scene/gui/graph_edit.h2
-rw-r--r--scene/gui/item_list.cpp38
-rw-r--r--scene/gui/item_list.h4
-rw-r--r--scene/resources/curve.cpp97
-rw-r--r--scene/resources/curve.h2
-rw-r--r--scene/resources/default_theme/default_theme.cpp50
-rw-r--r--scene/resources/text_paragraph.cpp17
-rw-r--r--servers/text_server.cpp6
-rw-r--r--tests/test_curve.h35
17 files changed, 401 insertions, 176 deletions
diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml
index 99e121de75..571ffd592a 100644
--- a/doc/classes/ColorPicker.xml
+++ b/doc/classes/ColorPicker.xml
@@ -116,7 +116,7 @@
</theme_item>
<theme_item name="picker_cursor" data_type="icon" type="Texture2D">
</theme_item>
- <theme_item name="preset_bg" data_type="icon" type="Texture2D">
+ <theme_item name="sample_bg" data_type="icon" type="Texture2D">
</theme_item>
<theme_item name="screen_picker" data_type="icon" type="Texture2D">
The icon for the screen color picker button.
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index 06e98f7e57..e3e4a9fa7d 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -403,6 +403,9 @@
<member name="select_mode" type="int" setter="set_select_mode" getter="get_select_mode" enum="ItemList.SelectMode" default="0">
Allows single or multiple item selection. See the [enum SelectMode] constants.
</member>
+ <member name="text_overrun_behavior" type="int" setter="set_text_overrun_behavior" getter="get_text_overrun_behavior" enum="TextParagraph.OverrunBehavior" default="0">
+ Sets the clipping behavior when the text exceeds an item's bounding rectangle. See [enum TextParagraph.OverrunBehavior] for a description of all modes.
+ </member>
</members>
<signals>
<signal name="item_activated">
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index 8d1c22dabd..fb5f7448c4 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -64,35 +64,42 @@ void DocTools::merge_from(const DocTools &p_data) {
if (cf.methods[j].name != m.name) {
continue;
}
- if (cf.methods[j].arguments.size() != m.arguments.size()) {
- continue;
- }
- // since polymorphic functions are allowed we need to check the type of
- // the arguments so we make sure they are different.
- int arg_count = cf.methods[j].arguments.size();
- Vector<bool> arg_used;
- arg_used.resize(arg_count);
- for (int l = 0; l < arg_count; ++l) {
- arg_used.write[l] = false;
- }
- // also there is no guarantee that argument ordering will match, so we
- // have to check one by one so we make sure we have an exact match
- for (int k = 0; k < arg_count; ++k) {
+
+ const char *operator_prefix = "operator "; // Operators use a space at the end, making this prefix an invalid identifier (and differentiating from methods).
+
+ if (cf.methods[j].name == c.name || cf.methods[j].name.begins_with(operator_prefix)) {
+ // Since constructors and operators can repeat, we need to check the type of
+ // the arguments so we make sure they are different.
+
+ if (cf.methods[j].arguments.size() != m.arguments.size()) {
+ continue;
+ }
+
+ int arg_count = cf.methods[j].arguments.size();
+ Vector<bool> arg_used;
+ arg_used.resize(arg_count);
for (int l = 0; l < arg_count; ++l) {
- if (cf.methods[j].arguments[k].type == m.arguments[l].type && !arg_used[l]) {
- arg_used.write[l] = true;
- break;
+ arg_used.write[l] = false;
+ }
+ // also there is no guarantee that argument ordering will match, so we
+ // have to check one by one so we make sure we have an exact match
+ for (int k = 0; k < arg_count; ++k) {
+ for (int l = 0; l < arg_count; ++l) {
+ if (cf.methods[j].arguments[k].type == m.arguments[l].type && !arg_used[l]) {
+ arg_used.write[l] = true;
+ break;
+ }
}
}
- }
- bool not_the_same = false;
- for (int l = 0; l < arg_count; ++l) {
- if (!arg_used[l]) { // at least one of the arguments was different
- not_the_same = true;
+ bool not_the_same = false;
+ for (int l = 0; l < arg_count; ++l) {
+ if (!arg_used[l]) { // at least one of the arguments was different
+ not_the_same = true;
+ }
+ }
+ if (not_the_same) {
+ continue;
}
- }
- if (not_the_same) {
- continue;
}
const DocData::MethodDoc &mf = cf.methods[j];
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index 5ccbed1b49..8f94bb296d 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -728,6 +728,7 @@ void EditorFileDialog::update_file_list() {
item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
item_list->set_max_text_lines(2);
+ item_list->set_text_overrun_behavior(TextParagraph::OVERRUN_TRIM_ELLIPSIS);
item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
if (thumbnail_size < 64) {
@@ -1645,6 +1646,7 @@ EditorFileDialog::EditorFileDialog() {
item_list->connect("item_rmb_selected", callable_mp(this, &EditorFileDialog::_item_list_item_rmb_selected));
item_list->connect("rmb_clicked", callable_mp(this, &EditorFileDialog::_item_list_rmb_clicked));
item_list->set_allow_rmb_select(true);
+
list_vb->add_child(item_list);
item_menu = memnew(PopupMenu);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 4496180ddd..8c348731d6 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -1361,7 +1361,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("label_width", "ColorPicker", 10 * EDSCALE);
theme->set_icon("screen_picker", "ColorPicker", theme->get_icon("ColorPick", "EditorIcons"));
theme->set_icon("add_preset", "ColorPicker", theme->get_icon("Add", "EditorIcons"));
- theme->set_icon("preset_bg", "ColorPicker", theme->get_icon("GuiMiniCheckerboard", "EditorIcons"));
+ theme->set_icon("sample_bg", "ColorPicker", theme->get_icon("GuiMiniCheckerboard", "EditorIcons"));
theme->set_icon("overbright_indicator", "ColorPicker", theme->get_icon("OverbrightIndicator", "EditorIcons"));
theme->set_icon("bar_arrow", "ColorPicker", theme->get_icon("ColorPickerBarArrow", "EditorIcons"));
theme->set_icon("picker_cursor", "ColorPicker", theme->get_icon("PickerCursor", "EditorIcons"));
@@ -1369,6 +1369,13 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
// ColorPickerButton
theme->set_icon("bg", "ColorPickerButton", theme->get_icon("GuiMiniCheckerboard", "EditorIcons"));
+ // ColorPresetButton
+ Ref<StyleBoxFlat> preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2, 2);
+ preset_sb->set_anti_aliased(false);
+ theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb);
+ theme->set_icon("preset_bg", "ColorPresetButton", theme->get_icon("GuiMiniCheckerboard", "EditorIcons"));
+ theme->set_icon("overbright_indicator", "ColorPresetButton", theme->get_icon("OverbrightIndicator", "EditorIcons"));
+
// Information on 3D viewport
Ref<StyleBoxFlat> style_info_3d_viewport = style_default->duplicate();
style_info_3d_viewport->set_bg_color(style_info_3d_viewport->get_bg_color() * Color(1, 1, 1, 0.5));
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 261480bcdd..661e0dc648 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -46,13 +46,13 @@ void ColorPicker::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker")));
- bt_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
-
+ btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
+ _update_presets();
_update_controls();
} break;
case NOTIFICATION_ENTER_TREE: {
btn_pick->set_icon(get_theme_icon(SNAME("screen_picker"), SNAME("ColorPicker")));
- bt_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
+ btn_add_preset->set_icon(get_theme_icon(SNAME("add_preset")));
_update_controls();
_update_color();
@@ -69,7 +69,6 @@ void ColorPicker::_notification(int p_what) {
for (int i = 0; i < preset_cache.size(); i++) {
presets.push_back(preset_cache[i]);
}
- preset->update();
}
#endif
} break;
@@ -372,22 +371,23 @@ void ColorPicker::_update_color(bool p_update_sliders) {
}
void ColorPicker::_update_presets() {
- return;
- //presets should be shown using buttons or something else, this method is not a good idea
-
- presets_per_row = 10;
- Size2 size = bt_add_preset->get_size();
- Size2 preset_size = Size2(MIN(size.width * presets.size(), presets_per_row * size.width), size.height * (Math::ceil((float)presets.size() / presets_per_row)));
- preset->set_custom_minimum_size(preset_size);
- preset_container->set_custom_minimum_size(preset_size);
- preset->draw_rect(Rect2(Point2(), preset_size), Color(1, 1, 1, 0));
-
- for (int i = 0; i < presets.size(); i++) {
- int x = (i % presets_per_row) * size.width;
- int y = (Math::floor((float)i / presets_per_row)) * size.height;
- preset->draw_rect(Rect2(Point2(x, y), size), presets[i]);
+ int preset_size = _get_preset_size();
+ // Only update the preset button size if it has changed.
+ if (preset_size != prev_preset_size) {
+ prev_preset_size = preset_size;
+ btn_add_preset->set_custom_minimum_size(Size2(preset_size, preset_size));
+ for (int i = 1; i < preset_container->get_child_count(); i++) {
+ ColorPresetButton *cpb = Object::cast_to<ColorPresetButton>(preset_container->get_child(i));
+ cpb->set_custom_minimum_size(Size2(preset_size, preset_size));
+ }
+ }
+ // Only load preset buttons when the only child is the add-preset button.
+ if (preset_container->get_child_count() == 1) {
+ for (int i = 0; i < preset_cache.size(); i++) {
+ _add_preset_button(preset_size, preset_cache[i]);
+ }
+ _notification(NOTIFICATION_VISIBILITY_CHANGED);
}
- _notification(NOTIFICATION_VISIBILITY_CHANGED);
}
void ColorPicker::_text_type_toggled() {
@@ -422,14 +422,37 @@ ColorPicker::PickerShapeType ColorPicker::get_picker_shape() const {
return picker_type;
}
+inline int ColorPicker::_get_preset_size() {
+ return (int(get_minimum_size().width) - (preset_container->get_theme_constant(SNAME("hseparation")) * (preset_column_count - 1))) / preset_column_count;
+}
+
+void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
+ ColorPresetButton *btn_preset = memnew(ColorPresetButton(p_color));
+ btn_preset->set_preset_color(p_color);
+ btn_preset->set_custom_minimum_size(Size2(p_size, p_size));
+ btn_preset->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input), varray(p_color));
+ btn_preset->set_tooltip(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1)));
+ preset_container->add_child(btn_preset);
+}
+
void ColorPicker::add_preset(const Color &p_color) {
if (presets.find(p_color)) {
presets.move_to_back(presets.find(p_color));
+
+ // Find button to move to the end.
+ for (int i = 1; i < preset_container->get_child_count(); i++) {
+ ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(preset_container->get_child(i));
+ if (current_btn && p_color == current_btn->get_preset_color()) {
+ preset_container->move_child(current_btn, preset_container->get_child_count() - 1);
+ break;
+ }
+ }
} else {
presets.push_back(p_color);
preset_cache.push_back(p_color);
+
+ _add_preset_button(_get_preset_size(), p_color);
}
- preset->update();
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
@@ -443,7 +466,15 @@ void ColorPicker::erase_preset(const Color &p_color) {
if (presets.find(p_color)) {
presets.erase(presets.find(p_color));
preset_cache.erase(preset_cache.find(p_color));
- preset->update();
+
+ // Find preset button to remove.
+ for (int i = 1; i < preset_container->get_child_count(); i++) {
+ ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(preset_container->get_child(i));
+ if (current_btn && p_color == current_btn->get_preset_color()) {
+ current_btn->queue_delete();
+ break;
+ }
+ }
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
@@ -560,7 +591,7 @@ void ColorPicker::_sample_draw() {
const Rect2 rect_old = Rect2(Point2(), Size2(sample->get_size().width * 0.5, sample->get_size().height * 0.95));
if (display_old_color && old_color.a < 1.0) {
- sample->draw_texture_rect(get_theme_icon(SNAME("preset_bg"), SNAME("ColorPicker")), rect_old, true);
+ sample->draw_texture_rect(get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), rect_old, true);
}
sample->draw_rect(rect_old, old_color);
@@ -574,7 +605,7 @@ void ColorPicker::_sample_draw() {
}
if (color.a < 1.0) {
- sample->draw_texture_rect(get_theme_icon(SNAME("preset_bg"), SNAME("ColorPicker")), rect_new, true);
+ sample->draw_texture_rect(get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), rect_new, true);
}
sample->draw_rect(rect_new, color);
@@ -734,7 +765,7 @@ void ColorPicker::_slider_draw(int p_which) {
#endif
if (p_which == 3) {
- scroll[p_which]->draw_texture_rect(get_theme_icon(SNAME("preset_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true);
+ scroll[p_which]->draw_texture_rect(get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true);
left_color = color;
left_color.a = 0;
@@ -932,43 +963,19 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
}
}
-void ColorPicker::_preset_input(const Ref<InputEvent> &p_event) {
+void ColorPicker::_preset_input(const Ref<InputEvent> &p_event, const Color &p_color) {
Ref<InputEventMouseButton> bev = p_event;
if (bev.is_valid()) {
- int index = 0;
if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_LEFT) {
- for (int i = 0; i < presets.size(); i++) {
- int x = (i % presets_per_row) * bt_add_preset->get_size().x;
- int y = (Math::floor((float)i / presets_per_row)) * bt_add_preset->get_size().y;
- if (bev->get_position().x > x && bev->get_position().x < x + preset->get_size().x && bev->get_position().y > y && bev->get_position().y < y + preset->get_size().y) {
- index = i;
- }
- }
- set_pick_color(presets[index]);
+ set_pick_color(p_color);
_update_color();
- emit_signal(SNAME("color_changed"), color);
+ emit_signal(SNAME("color_changed"), p_color);
} else if (bev->is_pressed() && bev->get_button_index() == MOUSE_BUTTON_RIGHT && presets_enabled) {
- index = bev->get_position().x / (preset->get_size().x / presets.size());
- Color clicked_preset = presets[index];
- erase_preset(clicked_preset);
- emit_signal(SNAME("preset_removed"), clicked_preset);
- bt_add_preset->show();
+ erase_preset(p_color);
+ emit_signal(SNAME("preset_removed"), p_color);
}
}
-
- Ref<InputEventMouseMotion> mev = p_event;
-
- if (mev.is_valid()) {
- int index = mev->get_position().x * presets.size();
- if (preset->get_size().x != 0) {
- index /= preset->get_size().x;
- }
- if (index < 0 || index >= presets.size()) {
- return;
- }
- preset->set_tooltip(vformat(RTR("Color: #%s\nLMB: Set color\nRMB: Remove preset"), presets[index].to_html(presets[index].a < 1)));
- }
}
void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) {
@@ -1064,11 +1071,11 @@ void ColorPicker::_html_focus_exit() {
void ColorPicker::set_presets_enabled(bool p_enabled) {
presets_enabled = p_enabled;
if (!p_enabled) {
- bt_add_preset->set_disabled(true);
- bt_add_preset->set_focus_mode(FOCUS_NONE);
+ btn_add_preset->set_disabled(true);
+ btn_add_preset->set_focus_mode(FOCUS_NONE);
} else {
- bt_add_preset->set_disabled(false);
- bt_add_preset->set_focus_mode(FOCUS_ALL);
+ btn_add_preset->set_disabled(false);
+ btn_add_preset->set_focus_mode(FOCUS_ALL);
}
}
@@ -1080,7 +1087,6 @@ void ColorPicker::set_presets_visible(bool p_visible) {
presets_visible = p_visible;
preset_separator->set_visible(p_visible);
preset_container->set_visible(p_visible);
- preset_container2->set_visible(p_visible);
}
bool ColorPicker::are_presets_visible() const {
@@ -1266,17 +1272,13 @@ ColorPicker::ColorPicker() :
add_child(preset_separator);
preset_container->set_h_size_flags(SIZE_EXPAND_FILL);
+ preset_container->set_columns(preset_column_count);
add_child(preset_container);
- preset_container->add_child(preset);
- preset->connect("gui_input", callable_mp(this, &ColorPicker::_preset_input));
- preset->connect("draw", callable_mp(this, &ColorPicker::_update_presets));
-
- preset_container2->set_h_size_flags(SIZE_EXPAND_FILL);
- add_child(preset_container2);
- preset_container2->add_child(bt_add_preset);
- bt_add_preset->set_tooltip(RTR("Add current color as a preset."));
- bt_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed));
+ btn_add_preset->set_icon_align(Button::ALIGN_CENTER);
+ btn_add_preset->set_tooltip(RTR("Add current color as a preset."));
+ btn_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed));
+ preset_container->add_child(btn_add_preset);
}
/////////////////
@@ -1303,6 +1305,7 @@ void ColorPickerButton::pressed() {
_update_picker();
popup->set_as_minsize();
+ picker->_update_presets();
Rect2i usable_rect = popup->get_usable_parent_rect();
//let's try different positions to see which one we can use
@@ -1428,3 +1431,64 @@ void ColorPickerButton::_bind_methods() {
ColorPickerButton::ColorPickerButton() {
set_toggle_mode(true);
}
+
+/////////////////
+
+void ColorPresetButton::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ const Rect2 r = Rect2(Point2(0, 0), get_size());
+ Ref<StyleBox> sb_raw = get_theme_stylebox(SNAME("preset_fg"), SNAME("ColorPresetButton"))->duplicate();
+ Ref<StyleBoxFlat> sb_flat = sb_raw;
+ Ref<StyleBoxTexture> sb_texture = sb_raw;
+
+ if (sb_flat.is_valid()) {
+ if (preset_color.a < 1) {
+ // Draw a background pattern when the color is transparent.
+ sb_flat->set_bg_color(Color(1, 1, 1));
+ sb_flat->draw(get_canvas_item(), r);
+
+ Rect2 bg_texture_rect = r.grow_side(SIDE_LEFT, -sb_flat->get_margin(SIDE_LEFT));
+ bg_texture_rect = bg_texture_rect.grow_side(SIDE_RIGHT, -sb_flat->get_margin(SIDE_RIGHT));
+ bg_texture_rect = bg_texture_rect.grow_side(SIDE_TOP, -sb_flat->get_margin(SIDE_TOP));
+ bg_texture_rect = bg_texture_rect.grow_side(SIDE_BOTTOM, -sb_flat->get_margin(SIDE_BOTTOM));
+
+ draw_texture_rect(get_theme_icon(SNAME("preset_bg"), SNAME("ColorPresetButton")), bg_texture_rect, true);
+ sb_flat->set_bg_color(preset_color);
+ }
+ sb_flat->set_bg_color(preset_color);
+ sb_flat->draw(get_canvas_item(), r);
+ } else if (sb_texture.is_valid()) {
+ if (preset_color.a < 1) {
+ // Draw a background pattern when the color is transparent.
+ bool use_tile_texture = (sb_texture->get_h_axis_stretch_mode() == StyleBoxTexture::AxisStretchMode::AXIS_STRETCH_MODE_TILE) || (sb_texture->get_h_axis_stretch_mode() == StyleBoxTexture::AxisStretchMode::AXIS_STRETCH_MODE_TILE_FIT);
+ draw_texture_rect(get_theme_icon(SNAME("preset_bg"), SNAME("ColorPresetButton")), r, use_tile_texture);
+ }
+ sb_texture->set_modulate(preset_color);
+ sb_texture->draw(get_canvas_item(), r);
+ } else {
+ WARN_PRINT("Unsupported StyleBox used for ColorPresetButton. Use StyleBoxFlat or StyleBoxTexture instead.");
+ }
+ if (preset_color.r > 1 || preset_color.g > 1 || preset_color.b > 1) {
+ // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview
+ draw_texture(Control::get_theme_icon(SNAME("overbright_indicator"), SNAME("ColorPresetButton")), Vector2(0, 0));
+ }
+
+ } break;
+ }
+}
+
+void ColorPresetButton::set_preset_color(const Color &p_color) {
+ preset_color = p_color;
+}
+
+Color ColorPresetButton::get_preset_color() const {
+ return preset_color;
+}
+
+ColorPresetButton::ColorPresetButton(Color p_color) {
+ preset_color = p_color;
+}
+
+ColorPresetButton::~ColorPresetButton() {
+}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 60da3957aa..67ca007eb5 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -35,6 +35,7 @@
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/check_button.h"
+#include "scene/gui/grid_container.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/popup.h"
@@ -43,6 +44,22 @@
#include "scene/gui/spin_box.h"
#include "scene/gui/texture_rect.h"
+class ColorPresetButton : public BaseButton {
+ GDCLASS(ColorPresetButton, BaseButton);
+
+ Color preset_color;
+
+protected:
+ void _notification(int);
+
+public:
+ void set_preset_color(const Color &p_color);
+ Color get_preset_color() const;
+
+ ColorPresetButton(Color p_color);
+ ~ColorPresetButton();
+};
+
class ColorPicker : public BoxContainer {
GDCLASS(ColorPicker, BoxContainer);
@@ -69,12 +86,9 @@ private:
Control *wheel = memnew(Control);
Control *wheel_uv = memnew(Control);
TextureRect *sample = memnew(TextureRect);
- TextureRect *preset = memnew(TextureRect);
- HBoxContainer *preset_container = memnew(HBoxContainer);
- HBoxContainer *preset_container2 = memnew(HBoxContainer);
+ GridContainer *preset_container = memnew(GridContainer);
HSeparator *preset_separator = memnew(HSeparator);
- Button *bt_add_preset = memnew(Button);
- List<Color> presets;
+ Button *btn_add_preset = memnew(Button);
Button *btn_pick = memnew(Button);
CheckButton *btn_hsv = memnew(CheckButton);
CheckButton *btn_raw = memnew(CheckButton);
@@ -83,14 +97,19 @@ private:
Label *labels[4];
Button *text_type = memnew(Button);
LineEdit *c_text = memnew(LineEdit);
+
bool edit_alpha = true;
Size2i ms;
bool text_is_constructor = false;
- int presets_per_row = 0;
PickerShapeType picker_type = SHAPE_HSV_WHEEL;
+ const int preset_column_count = 9;
+ int prev_preset_size = 0;
+ List<Color> presets;
+
Color color;
Color old_color;
+
bool display_old_color = false;
bool raw_mode_enabled = false;
bool hsv_mode_enabled = false;
@@ -100,6 +119,7 @@ private:
bool spinning = false;
bool presets_enabled = true;
bool presets_visible = true;
+
float h = 0.0;
float s = 0.0;
float v = 0.0;
@@ -109,7 +129,6 @@ private:
void _value_changed(double);
void _update_controls();
void _update_color(bool p_update_sliders = true);
- void _update_presets();
void _update_text_value();
void _text_type_toggled();
void _sample_input(const Ref<InputEvent> &p_event);
@@ -119,7 +138,7 @@ private:
void _uv_input(const Ref<InputEvent> &p_event, Control *c);
void _w_input(const Ref<InputEvent> &p_event);
- void _preset_input(const Ref<InputEvent> &p_event);
+ void _preset_input(const Ref<InputEvent> &p_event, const Color &p_color);
void _screen_input(const Ref<InputEvent> &p_event);
void _add_preset_pressed();
void _screen_pick_pressed();
@@ -127,6 +146,9 @@ private:
void _focus_exit();
void _html_focus_exit();
+ inline int _get_preset_size();
+ void _add_preset_button(int p_size, const Color &p_color);
+
protected:
void _notification(int);
static void _bind_methods();
@@ -152,6 +174,7 @@ public:
void add_preset(const Color &p_color);
void erase_preset(const Color &p_color);
PackedColorArray get_presets() const;
+ void _update_presets();
void set_hsv_mode(bool p_enabled);
bool is_hsv_mode() const;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index f916a35be0..ac1dea5e94 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -819,19 +819,21 @@ PackedVector2Array GraphEdit::get_connection_line(const Vector2 &p_from, const V
return curve.tessellate();
}
-void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width) {
- Vector<Vector2> points = get_connection_line(p_from, p_to);
+void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom) {
+ Vector<Vector2> points = get_connection_line(p_from / p_zoom, p_to / p_zoom);
+ Vector<Vector2> scaled_points;
Vector<Color> colors;
float length = p_from.distance_to(p_to);
for (int i = 0; i < points.size(); i++) {
float d = p_from.distance_to(points[i]) / length;
colors.push_back(p_color.lerp(p_to_color, d));
+ scaled_points.push_back(points[i] * p_zoom);
}
#ifdef TOOLS_ENABLED
- p_where->draw_polyline_colors(points, colors, Math::floor(p_width * EDSCALE), lines_antialiased);
+ p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * EDSCALE), lines_antialiased);
#else
- p_where->draw_polyline_colors(points, colors, p_width, lines_antialiased);
+ p_where->draw_polyline_colors(scaled_points, colors, p_width, lines_antialiased);
#endif
}
@@ -878,7 +880,7 @@ void GraphEdit::_connections_layer_draw() {
color = color.lerp(activity_color, E->get().activity);
tocolor = tocolor.lerp(activity_color, E->get().activity);
}
- _draw_connection_line(connections_layer, frompos, topos, color, tocolor, lines_thickness);
+ _draw_connection_line(connections_layer, frompos, topos, color, tocolor, lines_thickness, zoom);
}
while (to_erase.size()) {
@@ -917,7 +919,7 @@ void GraphEdit::_top_layer_draw() {
if (!connecting_out) {
SWAP(pos, topos);
}
- _draw_connection_line(top_layer, pos, topos, col, col, lines_thickness);
+ _draw_connection_line(top_layer, pos, topos, col, col, lines_thickness, zoom);
}
if (box_selecting) {
@@ -1021,7 +1023,7 @@ void GraphEdit::_minimap_draw() {
from_color = from_color.lerp(activity_color, E.activity);
to_color = to_color.lerp(activity_color, E.activity);
}
- _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 1.0);
+ _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.1, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length());
}
// Draw the "camera" viewport.
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 67d461a4cb..7a9286be0f 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -170,7 +170,7 @@ private:
bool lines_antialiased = true;
PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to);
- void _draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width);
+ void _draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom);
void _graph_node_raised(Node *p_gn);
void _graph_node_moved(Node *p_gn);
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 258d65112a..c0e6e069cd 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -48,6 +48,8 @@ void ItemList::_shape(int p_idx) {
} else {
item.text_buf->set_flags(TextServer::BREAK_NONE);
}
+ item.text_buf->set_text_overrun_behavior(text_overrun_behavior);
+ item.text_buf->set_max_lines_visible(max_text_lines);
}
int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bool p_selectable) {
@@ -453,6 +455,7 @@ void ItemList::set_max_text_lines(int p_lines) {
for (int i = 0; i < items.size(); i++) {
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
items.write[i].text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND);
+ items.write[i].text_buf->set_max_lines_visible(p_lines);
} else {
items.write[i].text_buf->set_flags(TextServer::BREAK_NONE);
}
@@ -930,8 +933,14 @@ void ItemList::_notification(int p_what) {
}
if (items[i].text != "") {
+ int max_width = -1;
+ if (fixed_column_width) {
+ max_width = fixed_column_width;
+ } else if (same_column_width) {
+ max_width = items[i].rect_cache.size.x;
+ }
+ items.write[i].text_buf->set_width(max_width);
Size2 s = items[i].text_buf->get_size();
- //s.width=MIN(s.width,fixed_column_width);
if (icon_mode == ICON_MODE_TOP) {
minsize.x = MAX(minsize.x, s.width);
@@ -1139,11 +1148,8 @@ void ItemList::_notification(int p_what) {
if (icon_mode == ICON_MODE_TOP) {
pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2);
- pos.y += MIN(
- Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2),
- items[i].rect_cache.size.height - items[i].min_rect_cache.size.height);
- text_ofs.y = icon_size.height + icon_margin;
- text_ofs.y += items[i].rect_cache.size.height - items[i].min_rect_cache.size.height;
+ pos.y += icon_margin;
+ text_ofs.y = icon_size.height + icon_margin * 2;
} else {
pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2);
text_ofs.x = icon_size.width + icon_margin;
@@ -1210,7 +1216,6 @@ void ItemList::_notification(int p_what) {
text_ofs.x = size.width - text_ofs.x - max_len;
}
- items.write[i].text_buf->set_width(max_len);
items.write[i].text_buf->set_align(HALIGN_CENTER);
if (outline_size > 0 && font_outline_color.a > 0) {
@@ -1488,6 +1493,21 @@ bool ItemList::has_auto_height() const {
return auto_height;
}
+void ItemList::set_text_overrun_behavior(TextParagraph::OverrunBehavior p_behavior) {
+ if (text_overrun_behavior != p_behavior) {
+ text_overrun_behavior = p_behavior;
+ for (int i = 0; i < items.size(); i++) {
+ items.write[i].text_buf->set_text_overrun_behavior(p_behavior);
+ }
+ shape_changed = true;
+ update();
+ }
+}
+
+TextParagraph::OverrunBehavior ItemList::get_text_overrun_behavior() const {
+ return text_overrun_behavior;
+}
+
void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_item", "text", "icon", "selectable"), &ItemList::add_item, DEFVAL(Variant()), DEFVAL(true));
ClassDB::bind_method(D_METHOD("add_icon_item", "icon", "selectable"), &ItemList::add_icon_item, DEFVAL(true));
@@ -1599,6 +1619,9 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_items"), &ItemList::_set_items);
ClassDB::bind_method(D_METHOD("_get_items"), &ItemList::_get_items);
+ ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &ItemList::set_text_overrun_behavior);
+ ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &ItemList::get_text_overrun_behavior);
+
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Multi"), "set_select_mode", "get_select_mode");
@@ -1606,6 +1629,7 @@ void ItemList::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_text_lines", PROPERTY_HINT_RANGE, "1,10,1,or_greater"), "set_max_text_lines", "get_max_text_lines");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
ADD_GROUP("Columns", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_columns", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), "set_max_columns", "get_max_columns");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "same_column_width"), "set_same_column_width", "is_same_column_width");
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index 86a0174a20..8a234398c3 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -95,6 +95,7 @@ private:
SelectMode select_mode = SELECT_SINGLE;
IconMode icon_mode = ICON_MODE_LEFT;
VScrollBar *scroll_bar;
+ TextParagraph::OverrunBehavior text_overrun_behavior = TextParagraph::OVERRUN_NO_TRIMMING;
uint64_t search_time_msec = 0;
String search_string;
@@ -182,6 +183,9 @@ public:
void set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_color);
Color get_item_custom_fg_color(int p_idx) const;
+ void set_text_overrun_behavior(TextParagraph::OverrunBehavior p_behavior);
+ TextParagraph::OverrunBehavior get_text_overrun_behavior() const;
+
void select(int p_idx, bool p_single = true);
void deselect(int p_idx);
void deselect_all();
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 3b666640f8..a364a27e80 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -662,19 +662,27 @@ void Curve2D::_bake() const {
if (points.size() == 0) {
baked_point_cache.resize(0);
+ baked_dist_cache.resize(0);
return;
}
if (points.size() == 1) {
baked_point_cache.resize(1);
baked_point_cache.set(0, points[0].pos);
+
+ baked_dist_cache.resize(1);
+ baked_dist_cache.set(0, 0.0);
return;
}
Vector2 pos = points[0].pos;
+ float dist = 0.0;
+
List<Vector2> pointlist;
+ List<float> distlist;
pointlist.push_back(pos); //start always from origin
+ distlist.push_back(0.0);
for (int i = 0; i < points.size() - 1; i++) {
float step = 0.1; // at least 10 substeps ought to be enough?
@@ -712,7 +720,10 @@ void Curve2D::_bake() const {
pos = npp;
p = mid;
+ dist += d;
+
pointlist.push_back(pos);
+ distlist.push_back(dist);
} else {
p = np;
}
@@ -722,16 +733,20 @@ void Curve2D::_bake() const {
Vector2 lastpos = points[points.size() - 1].pos;
float rem = pos.distance_to(lastpos);
- baked_max_ofs = (pointlist.size() - 1) * bake_interval + rem;
+ dist += rem;
+ baked_max_ofs = dist;
pointlist.push_back(lastpos);
+ distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
+ baked_dist_cache.resize(distlist.size());
+
Vector2 *w = baked_point_cache.ptrw();
- int idx = 0;
+ float *wd = baked_dist_cache.ptrw();
- for (const Vector2 &E : pointlist) {
- w[idx] = E;
- idx++;
+ for (int i = 0; i < pointlist.size(); i++) {
+ w[i] = pointlist[i];
+ wd[i] = distlist[i];
}
}
@@ -766,19 +781,26 @@ Vector2 Curve2D::interpolate_baked(float p_offset, bool p_cubic) const {
return r[bpc - 1];
}
- int idx = Math::floor((double)p_offset / (double)bake_interval);
- float frac = Math::fmod(p_offset, (float)bake_interval);
-
- if (idx >= bpc - 1) {
- return r[bpc - 1];
- } else if (idx == bpc - 2) {
- if (frac > 0) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ int start = 0, end = bpc, idx = (end + start) / 2;
+ // binary search to find baked points
+ while (start < idx) {
+ float offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
}
- } else {
- frac /= bake_interval;
+ idx = (end + start) / 2;
}
+ float offset_begin = baked_dist_cache[idx];
+ float offset_end = baked_dist_cache[idx + 1];
+
+ float idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector2(), "failed to find baked segment");
+
+ float frac = (p_offset - offset_begin) / idx_interval;
+
if (p_cubic) {
Vector2 pre = idx > 0 ? r[idx - 1] : r[idx];
Vector2 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
@@ -1145,6 +1167,7 @@ void Curve3D::_bake() const {
baked_point_cache.resize(0);
baked_tilt_cache.resize(0);
baked_up_vector_cache.resize(0);
+ baked_dist_cache.resize(0);
return;
}
@@ -1153,6 +1176,8 @@ void Curve3D::_bake() const {
baked_point_cache.set(0, points[0].pos);
baked_tilt_cache.resize(1);
baked_tilt_cache.set(0, points[0].tilt);
+ baked_dist_cache.resize(1);
+ baked_dist_cache.set(0, 0.0);
if (up_vector_enabled) {
baked_up_vector_cache.resize(1);
@@ -1165,8 +1190,12 @@ void Curve3D::_bake() const {
}
Vector3 pos = points[0].pos;
+ float dist = 0.0;
List<Plane> pointlist;
+ List<float> distlist;
+
pointlist.push_back(Plane(pos, points[0].tilt));
+ distlist.push_back(0.0);
for (int i = 0; i < points.size() - 1; i++) {
float step = 0.1; // at least 10 substeps ought to be enough?
@@ -1207,7 +1236,10 @@ void Curve3D::_bake() const {
Plane post;
post.normal = pos;
post.d = Math::lerp(points[i].tilt, points[i + 1].tilt, mid);
+ dist += d;
+
pointlist.push_back(post);
+ distlist.push_back(dist);
} else {
p = np;
}
@@ -1218,8 +1250,10 @@ void Curve3D::_bake() const {
float lastilt = points[points.size() - 1].tilt;
float rem = pos.distance_to(lastpos);
- baked_max_ofs = (pointlist.size() - 1) * bake_interval + rem;
+ dist += rem;
+ baked_max_ofs = dist;
pointlist.push_back(Plane(lastpos, lastilt));
+ distlist.push_back(dist);
baked_point_cache.resize(pointlist.size());
Vector3 *w = baked_point_cache.ptrw();
@@ -1231,6 +1265,9 @@ void Curve3D::_bake() const {
baked_up_vector_cache.resize(up_vector_enabled ? pointlist.size() : 0);
Vector3 *up_write = baked_up_vector_cache.ptrw();
+ baked_dist_cache.resize(pointlist.size());
+ float *wd = baked_dist_cache.ptrw();
+
Vector3 sideways;
Vector3 up;
Vector3 forward;
@@ -1242,6 +1279,7 @@ void Curve3D::_bake() const {
for (const Plane &E : pointlist) {
w[idx] = E.normal;
wt[idx] = E.d;
+ wd[idx] = distlist[idx];
if (!up_vector_enabled) {
idx++;
@@ -1308,19 +1346,26 @@ Vector3 Curve3D::interpolate_baked(float p_offset, bool p_cubic) const {
return r[bpc - 1];
}
- int idx = Math::floor((double)p_offset / (double)bake_interval);
- float frac = Math::fmod(p_offset, bake_interval);
-
- if (idx >= bpc - 1) {
- return r[bpc - 1];
- } else if (idx == bpc - 2) {
- if (frac > 0) {
- frac /= Math::fmod(baked_max_ofs, bake_interval);
+ int start = 0, end = bpc, idx = (end + start) / 2;
+ // binary search to find baked points
+ while (start < idx) {
+ float offset = baked_dist_cache[idx];
+ if (p_offset <= offset) {
+ end = idx;
+ } else {
+ start = idx;
}
- } else {
- frac /= bake_interval;
+ idx = (end + start) / 2;
}
+ float offset_begin = baked_dist_cache[idx];
+ float offset_end = baked_dist_cache[idx + 1];
+
+ float idx_interval = offset_end - offset_begin;
+ ERR_FAIL_COND_V_MSG(p_offset < offset_begin || p_offset > offset_end, Vector3(), "failed to find baked segment");
+
+ float frac = (p_offset - offset_begin) / idx_interval;
+
if (p_cubic) {
Vector3 pre = idx > 0 ? r[idx - 1] : r[idx];
Vector3 post = (idx < (bpc - 2)) ? r[idx + 2] : r[idx + 1];
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index c25d307608..5808fd6508 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -161,6 +161,7 @@ class Curve2D : public Resource {
mutable bool baked_cache_dirty = false;
mutable PackedVector2Array baked_point_cache;
+ mutable PackedFloat32Array baked_dist_cache;
mutable float baked_max_ofs = 0.0;
void _bake() const;
@@ -224,6 +225,7 @@ class Curve3D : public Resource {
mutable PackedVector3Array baked_point_cache;
mutable Vector<real_t> baked_tilt_cache;
mutable PackedVector3Array baked_up_vector_cache;
+ mutable PackedFloat32Array baked_dist_cache;
mutable float baked_max_ofs = 0.0;
void _bake() const;
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 4e139adabb..dd7b348498 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -212,26 +212,6 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("outline_size", "LinkButton", 0);
theme->set_constant("underline_spacing", "LinkButton", 2 * scale);
- // ColorPickerButton
-
- theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal);
- theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed);
- theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover);
- theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled);
- theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus);
-
- theme->set_font("font", "ColorPickerButton", Ref<Font>());
- theme->set_font_size("font_size", "ColorPickerButton", -1);
-
- theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1));
- theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1));
- theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1));
- theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
- theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
-
- theme->set_constant("hseparation", "ColorPickerButton", 2 * scale);
- theme->set_constant("outline_size", "ColorPickerButton", 0);
-
// OptionButton
Ref<StyleBox> sb_optbutton_focus = sb_expand(make_stylebox(button_focus_png, 4, 4, 4, 4, 6, 2, 6, 2), 2, 2, 2, 2);
@@ -859,7 +839,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("add_preset", "ColorPicker", make_icon(icon_add_png));
theme->set_icon("color_hue", "ColorPicker", make_icon(color_picker_hue_png));
theme->set_icon("color_sample", "ColorPicker", make_icon(color_picker_sample_png));
- theme->set_icon("preset_bg", "ColorPicker", make_icon(mini_checkerboard_png));
+ theme->set_icon("sample_bg", "ColorPicker", make_icon(mini_checkerboard_png));
theme->set_icon("overbright_indicator", "ColorPicker", make_icon(overbright_indicator_png));
theme->set_icon("bar_arrow", "ColorPicker", make_icon(bar_arrow_png));
theme->set_icon("picker_cursor", "ColorPicker", make_icon(picker_cursor_png));
@@ -867,6 +847,34 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
// ColorPickerButton
theme->set_icon("bg", "ColorPickerButton", make_icon(mini_checkerboard_png));
+ theme->set_stylebox("normal", "ColorPickerButton", sb_button_normal);
+ theme->set_stylebox("pressed", "ColorPickerButton", sb_button_pressed);
+ theme->set_stylebox("hover", "ColorPickerButton", sb_button_hover);
+ theme->set_stylebox("disabled", "ColorPickerButton", sb_button_disabled);
+ theme->set_stylebox("focus", "ColorPickerButton", sb_button_focus);
+
+ theme->set_font("font", "ColorPickerButton", Ref<Font>());
+ theme->set_font_size("font_size", "ColorPickerButton", -1);
+
+ theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1));
+ theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1));
+ theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1));
+ theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
+ theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
+
+ theme->set_constant("hseparation", "ColorPickerButton", 2 * scale);
+ theme->set_constant("outline_size", "ColorPickerButton", 0);
+
+ // ColorPresetButton
+
+ Ref<StyleBoxFlat> preset_sb = make_flat_stylebox(Color(1, 1, 1), 2, 2, 2, 2);
+ preset_sb->set_corner_radius_all(2);
+ preset_sb->set_corner_detail(2);
+ preset_sb->set_anti_aliased(false);
+
+ theme->set_stylebox("preset_fg", "ColorPresetButton", preset_sb);
+ theme->set_icon("preset_bg", "ColorPresetButton", make_icon(mini_checkerboard_png));
+ theme->set_icon("overbright_indicator", "ColorPresetButton", make_icon(overbright_indicator_png));
// TooltipPanel
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 357411ae04..763033354a 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -437,7 +437,8 @@ Size2 TextParagraph::get_non_wraped_size() const {
Size2 TextParagraph::get_size() const {
const_cast<TextParagraph *>(this)->_shape_lines();
Size2 size;
- for (int i = 0; i < lines_rid.size(); i++) {
+ int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, lines_rid.size()) : lines_rid.size();
+ for (int i = 0; i < visible_lines; i++) {
Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
size.x = MAX(size.x, lsize.x);
@@ -587,15 +588,15 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
l_width -= h_offset;
}
}
- float length = TS->shaped_text_get_width(lines_rid[i]);
+ float line_width = TS->shaped_text_get_width(lines_rid[i]);
if (width > 0) {
switch (align) {
case HALIGN_FILL:
if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.x += l_width - length;
+ ofs.x += l_width - line_width;
} else {
- ofs.y += l_width - length;
+ ofs.y += l_width - line_width;
}
}
break;
@@ -603,16 +604,16 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
break;
case HALIGN_CENTER: {
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.x += Math::floor((l_width - length) / 2.0);
+ ofs.x += Math::floor((l_width - line_width) / 2.0);
} else {
- ofs.y += Math::floor((l_width - length) / 2.0);
+ ofs.y += Math::floor((l_width - line_width) / 2.0);
}
} break;
case HALIGN_RIGHT: {
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
- ofs.x += l_width - length;
+ ofs.x += l_width - line_width;
} else {
- ofs.y += l_width - length;
+ ofs.y += l_width - line_width;
}
} break;
}
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index 6bace8cf9e..e51e229418 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -665,9 +665,7 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, float p_w
continue;
}
if (l_gl[i].count > 0) {
- //Ignore trailing spaces.
- bool is_space = (l_gl[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE;
- if ((p_width > 0) && (width + (is_space ? 0 : l_gl[i].advance) > p_width) && (last_safe_break >= 0)) {
+ if ((p_width > 0) && (width + l_gl[i].advance * l_gl[i].repeat > p_width) && (last_safe_break >= 0)) {
lines.push_back(Vector2i(line_start, l_gl[last_safe_break].end));
line_start = l_gl[last_safe_break].end;
i = last_safe_break;
@@ -698,7 +696,7 @@ Vector<Vector2i> TextServer::shaped_text_get_line_breaks(RID p_shaped, float p_w
last_safe_break = i;
}
}
- width += l_gl[i].advance;
+ width += l_gl[i].advance * l_gl[i].repeat;
}
if (l_size > 0) {
diff --git a/tests/test_curve.h b/tests/test_curve.h
index 7eeee86f32..e079905e35 100644
--- a/tests/test_curve.h
+++ b/tests/test_curve.h
@@ -216,6 +216,41 @@ TEST_CASE("[Curve] Custom curve with linear tangents") {
Math::is_equal_approx(curve->interpolate_baked(0.7), (real_t)0.8),
"Custom free curve should return the expected baked value at offset 0.7 after removing point at invalid index 10.");
}
+
+TEST_CASE("[Curve2D] Linear sampling should return exact value") {
+ Ref<Curve2D> curve = memnew(Curve2D);
+ int len = 2048;
+
+ curve->add_point(Vector2(0, 0));
+ curve->add_point(Vector2((float)len, 0));
+
+ float baked_length = curve->get_baked_length();
+ CHECK((float)len == baked_length);
+
+ for (int i = 0; i < len; i++) {
+ float expected = (float)i;
+ Vector2 pos = curve->interpolate_baked(expected);
+ CHECK_MESSAGE(pos.x == expected, "interpolate_baked should return exact value");
+ }
+}
+
+TEST_CASE("[Curve3D] Linear sampling should return exact value") {
+ Ref<Curve3D> curve = memnew(Curve3D);
+ int len = 2048;
+
+ curve->add_point(Vector3(0, 0, 0));
+ curve->add_point(Vector3((float)len, 0, 0));
+
+ float baked_length = curve->get_baked_length();
+ CHECK((float)len == baked_length);
+
+ for (int i = 0; i < len; i++) {
+ float expected = (float)i;
+ Vector3 pos = curve->interpolate_baked(expected);
+ CHECK_MESSAGE(pos.x == expected, "interpolate_baked should return exact value");
+ }
+}
+
} // namespace TestCurve
#endif // TEST_CURVE_H