summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/button.cpp85
-rw-r--r--scene/gui/button.h9
-rw-r--r--scene/gui/code_edit.cpp79
-rw-r--r--scene/gui/code_edit.h5
-rw-r--r--scene/gui/color_mode.cpp330
-rw-r--r--scene/gui/color_mode.h143
-rw-r--r--scene/gui/color_picker.cpp505
-rw-r--r--scene/gui/color_picker.h93
-rw-r--r--scene/gui/control.cpp47
-rw-r--r--scene/gui/file_dialog.cpp85
-rw-r--r--scene/gui/file_dialog.h6
-rw-r--r--scene/gui/gradient_edit.cpp6
-rw-r--r--scene/gui/gradient_edit.h4
-rw-r--r--scene/gui/graph_node.cpp72
-rw-r--r--scene/gui/graph_node.h5
-rw-r--r--scene/gui/item_list.cpp35
-rw-r--r--scene/gui/item_list.h5
-rw-r--r--scene/gui/label.cpp108
-rw-r--r--scene/gui/label.h9
-rw-r--r--scene/gui/line_edit.cpp136
-rw-r--r--scene/gui/line_edit.h12
-rw-r--r--scene/gui/link_button.cpp83
-rw-r--r--scene/gui/link_button.h9
-rw-r--r--scene/gui/popup_menu.cpp39
-rw-r--r--scene/gui/popup_menu.h4
-rw-r--r--scene/gui/rich_text_label.cpp185
-rw-r--r--scene/gui/rich_text_label.h9
-rw-r--r--scene/gui/spin_box.cpp2
-rw-r--r--scene/gui/tab_bar.cpp47
-rw-r--r--scene/gui/tab_bar.h5
-rw-r--r--scene/gui/tab_container.cpp1
-rw-r--r--scene/gui/text_edit.cpp175
-rw-r--r--scene/gui/text_edit.h21
-rw-r--r--scene/gui/tree.cpp71
-rw-r--r--scene/gui/tree.h10
-rw-r--r--scene/gui/video_stream_player.cpp34
-rw-r--r--scene/gui/video_stream_player.h3
37 files changed, 1232 insertions, 1245 deletions
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 1371c9cd57..d8de22d27c 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -347,7 +347,6 @@ void Button::_notification(int p_what) {
if (outline_size > 0 && font_outline_color.a > 0) {
text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color);
}
-
text_buf->draw(ci, text_ofs, color);
} break;
}
@@ -363,7 +362,7 @@ void Button::_shape() {
} else {
text_buf->set_direction((TextServer::Direction)text_direction);
}
- text_buf->add_string(xl_text, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text_buf->add_string(xl_text, font, font_size, language);
text_buf->set_text_overrun_behavior(overrun_behavior);
}
@@ -409,29 +408,6 @@ Control::TextDirection Button::get_text_direction() const {
return text_direction;
}
-void Button::clear_opentype_features() {
- opentype_features.clear();
- _shape();
- update();
-}
-
-void Button::set_opentype_feature(const String &p_name, int p_value) {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
- opentype_features[tag] = p_value;
- _shape();
- update();
- }
-}
-
-int Button::get_opentype_feature(const String &p_name) const {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag)) {
- return -1;
- }
- return opentype_features[tag];
-}
-
void Button::set_language(const String &p_language) {
if (language != p_language) {
language = p_language;
@@ -512,56 +488,6 @@ HorizontalAlignment Button::get_icon_alignment() const {
return icon_alignment;
}
-bool Button::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- int value = p_value;
- if (value == -1) {
- if (opentype_features.has(tag)) {
- opentype_features.erase(tag);
- _shape();
- update();
- }
- } else {
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
- opentype_features[tag] = value;
- _shape();
- update();
- }
- }
- notify_property_list_changed();
- return true;
- }
-
- return false;
-}
-
-bool Button::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- if (opentype_features.has(tag)) {
- r_ret = opentype_features[tag];
- return true;
- } else {
- r_ret = -1;
- return true;
- }
- }
- return false;
-}
-
-void Button::_get_property_list(List<PropertyInfo> *p_list) const {
- for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
- String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
- }
- p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
-}
-
void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_text", "text"), &Button::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text);
@@ -569,9 +495,6 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &Button::get_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Button::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction"), &Button::get_text_direction);
- ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Button::set_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Button::get_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Button::clear_opentype_features);
ClassDB::bind_method(D_METHOD("set_language", "language"), &Button::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &Button::get_language);
ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon);
@@ -588,8 +511,6 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
@@ -597,6 +518,10 @@ void Button::_bind_methods() {
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_PROPERTY(PropertyInfo(Variant::INT, "icon_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_alignment", "get_icon_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon");
+
+ ADD_GROUP("BiDi", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
}
Button::Button(const String &p_text) {
diff --git a/scene/gui/button.h b/scene/gui/button.h
index a1d71195cb..7a29cba677 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -43,7 +43,6 @@ private:
String xl_text;
Ref<TextParagraph> text_buf;
- Dictionary opentype_features;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
@@ -62,10 +61,6 @@ protected:
void _notification(int p_what);
static void _bind_methods();
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
public:
virtual Size2 get_minimum_size() const override;
@@ -78,10 +73,6 @@ public:
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
- void set_opentype_feature(const String &p_name, int p_value);
- int get_opentype_feature(const String &p_name) const;
- void clear_opentype_features();
-
void set_language(const String &p_language);
String get_language() const;
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 22e9763929..22f968eac7 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -46,7 +46,7 @@ void CodeEdit::_notification(int p_what) {
line_spacing = get_theme_constant(SNAME("line_spacing"));
set_gutter_width(main_gutter, get_line_height());
- set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', 0, font_size).width);
+ set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', font_size).width);
set_gutter_width(fold_gutter, get_line_height() / 1.2);
breakpoint_color = get_theme_color(SNAME("breakpoint_color"));
@@ -68,6 +68,7 @@ void CodeEdit::_notification(int p_what) {
code_completion_max_lines = get_theme_constant(SNAME("completion_lines"));
code_completion_scroll_width = get_theme_constant(SNAME("completion_scroll_width"));
code_completion_scroll_color = get_theme_color(SNAME("completion_scroll_color"));
+ code_completion_scroll_hovered_color = get_theme_color(SNAME("completion_scroll_hovered_color"));
code_completion_background_color = get_theme_color(SNAME("completion_background_color"));
code_completion_selected_color = get_theme_color(SNAME("completion_selected_color"));
code_completion_existing_color = get_theme_color(SNAME("completion_existing_color"));
@@ -85,7 +86,7 @@ void CodeEdit::_notification(int p_what) {
if (line_length_guideline_columns.size() > 0) {
const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width();
const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0);
- const float char_size = font->get_char_size('0', 0, font_size).width;
+ const float char_size = font->get_char_size('0', font_size).width;
for (int i = 0; i < line_length_guideline_columns.size(); i++) {
const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll();
@@ -122,7 +123,7 @@ void CodeEdit::_notification(int p_what) {
}
const int scroll_width = code_completion_options_count > code_completion_max_lines ? code_completion_scroll_width : 0;
- const int code_completion_base_width = font->get_string_size(code_completion_base, font_size).width;
+ const int code_completion_base_width = font->get_string_size(code_completion_base, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width;
if (caret_pos.x - code_completion_base_width + code_completion_rect.size.width + scroll_width > get_size().width) {
code_completion_rect.position.x = get_size().width - code_completion_rect.size.width - scroll_width;
} else {
@@ -134,6 +135,9 @@ void CodeEdit::_notification(int p_what) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(code_completion_rect.position, code_completion_rect.size + Size2(scroll_width, 0)), code_completion_background_color);
}
+ code_completion_scroll_rect.position = code_completion_rect.position + Vector2(code_completion_rect.size.width, 0);
+ code_completion_scroll_rect.size = Vector2(scroll_width, code_completion_rect.size.height);
+
code_completion_line_ofs = CLAMP(code_completion_current_selected - lines / 2, 0, code_completion_options_count - lines);
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), code_completion_selected_color);
@@ -174,8 +178,8 @@ void CodeEdit::_notification(int p_what) {
for (int j = 0; j < code_completion_options[l].matches.size(); j++) {
Pair<int, int> match = code_completion_options[l].matches[j];
- int match_offset = font->get_string_size(code_completion_options[l].display.substr(0, match.first), font_size).width;
- int match_len = font->get_string_size(code_completion_options[l].display.substr(match.first, match.second), font_size).width;
+ int match_offset = font->get_string_size(code_completion_options[l].display.substr(0, match.first), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width;
+ int match_len = font->get_string_size(code_completion_options[l].display.substr(match.first, match.second), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width;
draw_rect(Rect2(match_pos + Point2(match_offset, 0), Size2(match_len, row_height)), code_completion_existing_color);
}
@@ -185,9 +189,11 @@ void CodeEdit::_notification(int p_what) {
/* Draw a small scroll rectangle to show a position in the options. */
if (scroll_width) {
+ Color scroll_color = is_code_completion_scroll_hovered || is_code_completion_scroll_pressed ? code_completion_scroll_hovered_color : code_completion_scroll_color;
+
float r = (float)code_completion_max_lines / code_completion_options_count;
float o = (float)code_completion_line_ofs / code_completion_options_count;
- draw_rect(Rect2(code_completion_rect.position.x + code_completion_rect.size.width, code_completion_rect.position.y + o * code_completion_rect.size.y, scroll_width, code_completion_rect.size.y * r), code_completion_scroll_color);
+ draw_rect(Rect2(code_completion_rect.position.x + code_completion_rect.size.width, code_completion_rect.position.y + o * code_completion_rect.size.y, scroll_width, code_completion_rect.size.y * r), scroll_color);
}
}
@@ -202,11 +208,11 @@ void CodeEdit::_notification(int p_what) {
int max_width = 0;
for (int i = 0; i < line_count; i++) {
- max_width = MAX(max_width, font->get_string_size(code_hint_lines[i], font_size).x);
+ max_width = MAX(max_width, font->get_string_size(code_hint_lines[i], HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x);
}
Size2 minsize = sb->get_minimum_size() + Size2(max_width, line_count * font_height + (line_spacing * line_count - 1));
- int offset = font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), font_size).x;
+ int offset = font->get_string_size(code_hint_lines[0].substr(0, code_hint_lines[0].find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x;
if (code_hint_xpos == -0xFFFF) {
code_hint_xpos = get_caret_draw_pos().x - offset;
}
@@ -226,8 +232,8 @@ void CodeEdit::_notification(int p_what) {
int begin = 0;
int end = 0;
if (line.contains(String::chr(0xFFFF))) {
- begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), font_size).x;
- end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), font_size).x;
+ begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x;
+ end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x;
}
Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent(font_size) + font_height * i + yofs);
@@ -260,6 +266,12 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
return;
}
+ if (is_code_completion_scroll_pressed && mb->get_button_index() == MouseButton::LEFT) {
+ is_code_completion_scroll_pressed = false;
+ update();
+ return;
+ }
+
if (code_completion_active && code_completion_rect.has_point(mb->get_position())) {
if (!mb->is_pressed()) {
return;
@@ -289,7 +301,21 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
break;
}
return;
+ } else if (code_completion_active && code_completion_scroll_rect.has_point(mb->get_position())) {
+ if (mb->get_button_index() != MouseButton::LEFT) {
+ return;
+ }
+
+ if (mb->is_pressed()) {
+ is_code_completion_scroll_pressed = true;
+
+ _update_scroll_selected_line(mb->get_position().y);
+ update();
+ }
+
+ return;
}
+
cancel_code_completion();
set_code_hint("");
@@ -354,6 +380,18 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
set_symbol_lookup_word_as_valid(false);
}
}
+
+ bool scroll_hovered = code_completion_scroll_rect.has_point(mpos);
+ if (is_code_completion_scroll_hovered != scroll_hovered) {
+ is_code_completion_scroll_hovered = scroll_hovered;
+ update();
+ }
+
+ if (is_code_completion_scroll_pressed) {
+ _update_scroll_selected_line(mpos.y);
+ update();
+ return;
+ }
}
Ref<InputEventKey> k = p_gui_input;
@@ -546,6 +584,10 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
return CURSOR_ARROW;
}
+ if (code_completion_active && code_completion_scroll_rect.has_point(p_pos)) {
+ return CURSOR_ARROW;
+ }
+
Point2i pos = get_line_column_at_pos(p_pos, false);
int line = pos.y;
int col = pos.x;
@@ -2697,6 +2739,13 @@ TypedArray<String> CodeEdit::_get_delimiters(DelimiterType p_type) const {
}
/* Code Completion */
+void CodeEdit::_update_scroll_selected_line(float p_mouse_y) {
+ float percent = (float)(p_mouse_y - code_completion_scroll_rect.position.y) / code_completion_scroll_rect.size.height;
+ percent = CLAMP(percent, 0.0f, 1.0f);
+
+ code_completion_current_selected = (int)(percent * (code_completion_options.size() - 1));
+}
+
void CodeEdit::_filter_code_completion_candidates_impl() {
int line_height = get_line_height();
@@ -2746,7 +2795,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
offset = line_height;
}
- max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset);
+ max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
code_completion_options.push_back(option);
}
@@ -2857,7 +2906,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
if (string_to_complete.length() == 0) {
code_completion_options.push_back(option);
- max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset);
+ max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
continue;
}
@@ -2963,7 +3012,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
option.matches.append_array(ssq_matches);
completion_options_subseq.push_back(option);
}
- max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset);
+ max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
} else if (!*ssq_lower) { // Matched the whole subsequence in s_lower.
option.matches.clear();
@@ -2980,7 +3029,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
option.matches.append_array(ssq_lower_matches);
completion_options_subseq_casei.push_back(option);
}
- max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset);
+ max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset);
}
}
@@ -3036,7 +3085,7 @@ void CodeEdit::_text_changed() {
}
if (font.is_valid()) {
- set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', 0, font_size).width);
+ set_gutter_width(line_number_gutter, (line_number_digits + 1) * font->get_char_size('0', font_size).width);
}
lc = get_line_count();
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index ccf046c612..a431d8a5b2 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -203,22 +203,27 @@ private:
int code_completion_max_lines = 7;
int code_completion_scroll_width = 0;
Color code_completion_scroll_color = Color(0, 0, 0, 0);
+ Color code_completion_scroll_hovered_color = Color(0, 0, 0, 0);
Color code_completion_background_color = Color(0, 0, 0, 0);
Color code_completion_selected_color = Color(0, 0, 0, 0);
Color code_completion_existing_color = Color(0, 0, 0, 0);
bool code_completion_active = false;
+ bool is_code_completion_scroll_hovered = false;
+ bool is_code_completion_scroll_pressed = false;
Vector<ScriptLanguage::CodeCompletionOption> code_completion_options;
int code_completion_line_ofs = 0;
int code_completion_current_selected = 0;
int code_completion_longest_line = 0;
Rect2i code_completion_rect;
+ Rect2i code_completion_scroll_rect;
HashSet<char32_t> code_completion_prefixes;
List<ScriptLanguage::CodeCompletionOption> code_completion_option_submitted;
List<ScriptLanguage::CodeCompletionOption> code_completion_option_sources;
String code_completion_base;
+ void _update_scroll_selected_line(float p_mouse_y);
void _filter_code_completion_candidates_impl();
/* Line length guidelines */
diff --git a/scene/gui/color_mode.cpp b/scene/gui/color_mode.cpp
new file mode 100644
index 0000000000..af78d67e5a
--- /dev/null
+++ b/scene/gui/color_mode.cpp
@@ -0,0 +1,330 @@
+/*************************************************************************/
+/* color_mode.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "color_mode.h"
+
+#include "core/math/color.h"
+#include "scene/gui/slider.h"
+#include "thirdparty/misc/ok_color.h"
+
+ColorMode::ColorMode(ColorPicker *p_color_picker) {
+ color_picker = p_color_picker;
+}
+
+String ColorModeRGB::get_slider_label(int idx) const {
+ ERR_FAIL_INDEX_V_MSG(idx, 3, String(), "Couldn't get slider label.");
+ return labels[idx];
+}
+
+float ColorModeRGB::get_slider_max(int idx) const {
+ ERR_FAIL_INDEX_V_MSG(idx, 4, 0, "Couldn't get slider max value.");
+ Color color = color_picker->get_pick_color();
+ return next_power_of_2(MAX(255, color.components[idx] * 255.0)) - 1;
+}
+
+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;
+}
+
+Color ColorModeRGB::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] / 255.0;
+ }
+ return color;
+}
+
+void ColorModeRGB::slider_draw(int p_which) {
+ Vector<Vector2> pos;
+ pos.resize(4);
+ Vector<Color> col;
+ col.resize(4);
+ HSlider *slider = color_picker->get_slider(p_which);
+ Size2 size = slider->get_size();
+ Color left_color;
+ Color right_color;
+ Color color = color_picker->get_pick_color();
+ const real_t margin = 4 * color_picker->get_theme_default_base_scale();
+
+ if (p_which == ColorPicker::SLIDER_COUNT) {
+ slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true);
+
+ 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);
+ }
+
+ 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, margin));
+ pos.set(1, Vector2(size.x, margin));
+ pos.set(2, Vector2(size.x, margin * 2));
+ pos.set(3, Vector2(0, margin * 2));
+
+ slider->draw_polygon(pos, col);
+}
+
+String ColorModeHSV::get_slider_label(int idx) const {
+ ERR_FAIL_INDEX_V_MSG(idx, 3, 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.");
+ return slider_max[idx];
+}
+
+float ColorModeHSV::get_slider_value(int idx) const {
+ switch (idx) {
+ case 0:
+ return color_picker->get_pick_color().get_h() * 360.0;
+ case 1:
+ return color_picker->get_pick_color().get_s() * 100.0;
+ 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);
+ default:
+ ERR_FAIL_V_MSG(0, "Couldn't get slider value.");
+ }
+}
+
+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);
+ return color;
+}
+
+void ColorModeHSV::slider_draw(int p_which) {
+ Vector<Vector2> pos;
+ pos.resize(4);
+ Vector<Color> col;
+ col.resize(4);
+ HSlider *slider = color_picker->get_slider(p_which);
+ Size2 size = slider->get_size();
+ Color left_color;
+ Color right_color;
+ Color color = color_picker->get_pick_color();
+ const real_t margin = 4 * color_picker->get_theme_default_base_scale();
+
+ if (p_which == ColorPicker::SLIDER_COUNT) {
+ slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true);
+
+ left_color = color;
+ left_color.a = 0;
+ right_color = color;
+ right_color.a = 1;
+ } else if (p_which == 0) {
+ Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
+ slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0));
+ slider->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(slider->get_size().x, margin)), false, Color(1, 1, 1), true);
+ return;
+ } else {
+ Color s_col;
+ Color v_col;
+ s_col.set_hsv(color.get_h(), 0, color.get_v());
+ left_color = (p_which == 1) ? s_col : Color(0, 0, 0);
+ s_col.set_hsv(color.get_h(), 1, color.get_v());
+ v_col.set_hsv(color.get_h(), color.get_s(), 1);
+ right_color = (p_which == 1) ? s_col : v_col;
+ }
+ 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, margin));
+ pos.set(1, Vector2(size.x, margin));
+ pos.set(2, Vector2(size.x, margin * 2));
+ pos.set(3, Vector2(0, margin * 2));
+
+ slider->draw_polygon(pos, col);
+}
+
+String ColorModeRAW::get_slider_label(int idx) const {
+ ERR_FAIL_INDEX_V_MSG(idx, 3, 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.");
+ 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];
+}
+
+Color ColorModeRAW::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;
+}
+
+void ColorModeRAW::slider_draw(int p_which) {
+ Vector<Vector2> pos;
+ pos.resize(4);
+ Vector<Color> col;
+ col.resize(4);
+ HSlider *slider = color_picker->get_slider(p_which);
+ Size2 size = slider->get_size();
+ Color left_color;
+ Color right_color;
+ Color color = color_picker->get_pick_color();
+ const real_t margin = 4 * color_picker->get_theme_default_base_scale();
+
+ if (p_which == ColorPicker::SLIDER_COUNT) {
+ slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true);
+
+ 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, margin));
+ pos.set(1, Vector2(size.x, margin));
+ pos.set(2, Vector2(size.x, margin * 2));
+ pos.set(3, Vector2(0, margin * 2));
+
+ slider->draw_polygon(pos, col);
+ }
+}
+
+bool ColorModeRAW::apply_theme() const {
+ for (int i = 0; i < 4; 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_style_override("grabber_area");
+ slider->remove_theme_style_override("grabber_area_highlight");
+ }
+
+ return true;
+}
+
+String ColorModeOKHSL::get_slider_label(int idx) const {
+ ERR_FAIL_INDEX_V_MSG(idx, 3, 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.");
+ return slider_max[idx];
+}
+
+float ColorModeOKHSL::get_slider_value(int idx) const {
+ switch (idx) {
+ case 0:
+ return color_picker->get_pick_color().get_ok_hsl_h() * 360.0;
+ case 1:
+ return color_picker->get_pick_color().get_ok_hsl_s() * 100.0;
+ 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);
+ default:
+ ERR_FAIL_V_MSG(0, "Couldn't get slider value.");
+ }
+}
+
+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);
+ return color;
+}
+
+void ColorModeOKHSL::slider_draw(int p_which) {
+ Vector<Vector2> pos;
+ pos.resize(4);
+ Vector<Color> col;
+ col.resize(4);
+ HSlider *slider = color_picker->get_slider(p_which);
+ Size2 size = slider->get_size();
+ Color left_color;
+ Color right_color;
+ Color color = color_picker->get_pick_color();
+ const real_t margin = 4 * color_picker->get_theme_default_base_scale();
+
+ if (p_which == ColorPicker::SLIDER_COUNT) {
+ slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, margin), Size2(size.x, margin)), true);
+
+ left_color = color;
+ left_color.a = 0;
+ right_color = color;
+ right_color.a = 1;
+ } else if (p_which == 0) {
+ Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
+ slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0));
+ slider->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(slider->get_size().x, margin)), false, Color(1, 1, 1), true);
+ return;
+ } else {
+ Color s_col;
+ Color v_col;
+ s_col.set_ok_hsl(color.get_h(), 0, color.get_v());
+ left_color = (p_which == 1) ? s_col : Color(0, 0, 0);
+ s_col.set_ok_hsl(color.get_h(), 1, color.get_v());
+ v_col.set_ok_hsl(color.get_h(), color.get_s(), 1);
+ right_color = (p_which == 1) ? s_col : v_col;
+ }
+ 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, margin));
+ pos.set(1, Vector2(size.x, margin));
+ pos.set(2, Vector2(size.x, margin * 2));
+ pos.set(3, Vector2(0, margin * 2));
+
+ slider->draw_polygon(pos, col);
+}
diff --git a/scene/gui/color_mode.h b/scene/gui/color_mode.h
new file mode 100644
index 0000000000..8a19699c40
--- /dev/null
+++ b/scene/gui/color_mode.h
@@ -0,0 +1,143 @@
+/*************************************************************************/
+/* color_mode.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef COLOR_MODE_H
+#define COLOR_MODE_H
+
+#include "scene/gui/color_picker.h"
+
+struct Color;
+
+class ColorMode {
+public:
+ ColorPicker *color_picker = nullptr;
+
+ virtual String get_name() const = 0;
+
+ virtual int get_slider_count() const { return 3; };
+ virtual float get_slider_step() const = 0;
+ virtual String get_slider_label(int idx) const = 0;
+ virtual float get_slider_max(int idx) const = 0;
+ virtual float get_slider_value(int idx) const = 0;
+
+ virtual Color get_color() const = 0;
+
+ virtual void slider_draw(int p_which) = 0;
+ virtual bool apply_theme() const { return false; }
+ virtual ColorPicker::PickerShapeType get_shape_override() const { return ColorPicker::SHAPE_MAX; }
+
+ ColorMode(ColorPicker *p_color_picker);
+ virtual ~ColorMode(){};
+};
+
+class ColorModeHSV : public ColorMode {
+public:
+ String labels[3] = { "H", "S", "V" };
+ float slider_max[4] = { 359, 100, 100, 255 };
+
+ virtual String get_name() const override { return "HSV"; }
+
+ virtual float get_slider_step() const override { return 1.0; }
+ virtual String get_slider_label(int idx) const override;
+ virtual float get_slider_max(int idx) const override;
+ virtual float get_slider_value(int idx) const override;
+
+ virtual Color get_color() const override;
+
+ virtual void slider_draw(int p_which) override;
+
+ ColorModeHSV(ColorPicker *p_color_picker) :
+ ColorMode(p_color_picker){};
+};
+
+class ColorModeRGB : public ColorMode {
+public:
+ String labels[3] = { "R", "G", "B" };
+
+ virtual String get_name() const override { return "RGB"; }
+
+ virtual float get_slider_step() const override { return 1; }
+ virtual String get_slider_label(int idx) const override;
+ virtual float get_slider_max(int idx) const override;
+ virtual float get_slider_value(int idx) const override;
+
+ virtual Color get_color() const override;
+
+ virtual void slider_draw(int p_which) override;
+
+ ColorModeRGB(ColorPicker *p_color_picker) :
+ ColorMode(p_color_picker){};
+};
+
+class ColorModeRAW : public ColorMode {
+public:
+ String labels[3] = { "R", "G", "B" };
+ float slider_max[4] = { 100, 100, 100, 1 };
+
+ virtual String get_name() const override { return "RAW"; }
+
+ virtual float get_slider_step() const override { return 0.01; }
+ virtual String get_slider_label(int idx) const override;
+ virtual float get_slider_max(int idx) const override;
+ virtual float get_slider_value(int idx) const override;
+
+ virtual Color get_color() const override;
+
+ virtual void slider_draw(int p_which) override;
+ virtual bool apply_theme() const override;
+
+ ColorModeRAW(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 };
+
+ virtual String get_name() const override { return "OKHSL"; }
+
+ virtual float get_slider_step() const override { return 1.0; }
+ virtual String get_slider_label(int idx) const override;
+ virtual float get_slider_max(int idx) const override;
+ virtual float get_slider_value(int idx) const override;
+
+ virtual Color get_color() const override;
+
+ virtual void slider_draw(int p_which) override;
+ virtual ColorPicker::PickerShapeType get_shape_override() const override { return ColorPicker::SHAPE_OKHSL_CIRCLE; }
+
+ ColorModeOKHSL(ColorPicker *p_color_picker) :
+ ColorMode(p_color_picker){};
+
+ ~ColorModeOKHSL(){};
+};
+
+#endif // COLOR_MODE_H
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 28d645e8f6..492a81f933 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -34,7 +34,7 @@
#include "core/math/color.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "scene/main/window.h"
+#include "scene/gui/color_mode.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -75,10 +75,14 @@ void ColorPicker::_notification(int p_what) {
wheel_edit->set_custom_minimum_size(Size2(get_theme_constant(SNAME("sv_width")), get_theme_constant(SNAME("sv_height"))));
wheel_margin->add_theme_constant_override("margin_bottom", 8 * get_theme_default_base_scale());
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < SLIDER_COUNT; i++) {
labels[i]->set_custom_minimum_size(Size2(get_theme_constant(SNAME("label_width")), 0));
set_offset((Side)i, get_offset((Side)i) + get_theme_constant(SNAME("margin")));
}
+ alpha_label->set_custom_minimum_size(Size2(get_theme_constant(SNAME("label_width")), 0));
+ set_offset((Side)0, get_offset((Side)0) + get_theme_constant(SNAME("margin")));
+
+ _reset_theme();
if (Engine::get_singleton()->is_editor_hint()) {
// Adjust for the width of the "Script" icon.
@@ -194,75 +198,38 @@ void ColorPicker::set_focus_on_line_edit() {
}
void ColorPicker::_update_controls() {
- const char *rgb[3] = { "R", "G", "B" };
- const char *hsv[3] = { "H", "S", "V" };
- const char *hsl[3] = { "H", "S", "L" };
- if (hsv_mode_enabled && picker_type == SHAPE_OKHSL_CIRCLE) {
- for (int i = 0; i < 3; i++) {
- labels[i]->set_text(hsl[i]);
- }
- } else if (hsv_mode_enabled && picker_type != SHAPE_OKHSL_CIRCLE) {
- for (int i = 0; i < 3; i++) {
- labels[i]->set_text(hsv[i]);
- }
- } else {
- for (int i = 0; i < 3; i++) {
- labels[i]->set_text(rgb[i]);
- }
+ int mode_sliders_count = modes[current_mode]->get_slider_count();
+
+ for (int i = current_slider_count; i < mode_sliders_count; i++) {
+ sliders[i]->show();
+ labels[i]->show();
+ values[i]->show();
}
- if (picker_type == SHAPE_OKHSL_CIRCLE) {
- btn_hsv->set_text(RTR("OKHSL"));
- } else {
- btn_hsv->set_text(RTR("HSV"));
- }
- if (hsv_mode_enabled) {
- set_raw_mode(false);
- set_hsv_mode(true);
- btn_raw->set_disabled(true);
- } else if (raw_mode_enabled) {
- set_raw_mode(true);
- set_hsv_mode(false);
- btn_raw->set_disabled(false);
- btn_hsv->set_disabled(true);
- } else {
- set_raw_mode(false);
- set_hsv_mode(false);
- btn_raw->set_disabled(false);
- btn_hsv->set_disabled(false);
- }
-
- if (raw_mode_enabled) {
- for (int i = 0; i < 3; i++) {
- scroll[i]->remove_theme_icon_override("grabber");
- scroll[i]->remove_theme_icon_override("grabber_highlight");
- scroll[i]->remove_theme_style_override("slider");
- scroll[i]->remove_theme_style_override("grabber_area");
- scroll[i]->remove_theme_style_override("grabber_area_highlight");
- }
- } else {
- Ref<StyleBoxEmpty> style_box_empty(memnew(StyleBoxEmpty));
- Ref<Texture2D> bar_arrow = get_theme_icon(SNAME("bar_arrow"));
-
- for (int i = 0; i < 4; i++) {
- scroll[i]->add_theme_icon_override("grabber", bar_arrow);
- scroll[i]->add_theme_icon_override("grabber_highlight", bar_arrow);
- scroll[i]->add_theme_style_override("slider", style_box_empty);
- scroll[i]->add_theme_style_override("grabber_area", style_box_empty);
- scroll[i]->add_theme_style_override("grabber_area_highlight", style_box_empty);
- }
+ for (int i = mode_sliders_count; i < current_slider_count; i++) {
+ sliders[i]->hide();
+ labels[i]->hide();
+ values[i]->hide();
}
+ current_slider_count = mode_sliders_count;
+
+ for (int i = 0; i < current_slider_count; i++) {
+ labels[i]->set_text(modes[current_mode]->get_slider_label(i));
+ }
+ alpha_label->set_text("A");
+
+ slider_theme_modified = modes[current_mode]->apply_theme();
if (edit_alpha) {
- values[3]->show();
- scroll[3]->show();
- labels[3]->show();
+ alpha_value->show();
+ alpha_slider->show();
+ alpha_label->show();
} else {
- values[3]->hide();
- scroll[3]->hide();
- labels[3]->hide();
+ alpha_value->hide();
+ alpha_slider->hide();
+ alpha_label->hide();
}
- switch (picker_type) {
+ switch (_get_actual_shape()) {
case SHAPE_HSV_RECTANGLE:
wheel_edit->hide();
w_edit->show();
@@ -297,7 +264,7 @@ void ColorPicker::_update_controls() {
void ColorPicker::_set_pick_color(const Color &p_color, bool p_update_sliders) {
color = p_color;
if (color != last_color) {
- if (picker_type == SHAPE_OKHSL_CIRCLE) {
+ if (_get_actual_shape() == SHAPE_OKHSL_CIRCLE) {
h = color.get_ok_hsl_h();
s = color.get_ok_hsl_s();
v = color.get_ok_hsl_l();
@@ -353,27 +320,93 @@ void ColorPicker::_value_changed(double) {
return;
}
- if (hsv_mode_enabled) {
- h = scroll[0]->get_value() / 360.0;
- s = scroll[1]->get_value() / 100.0;
- v = scroll[2]->get_value() / 100.0;
- if (picker_type == SHAPE_OKHSL_CIRCLE) {
- color.set_ok_hsl(h, s, v, Math::round(scroll[3]->get_value() / 255.0));
- } else {
- color.set_hsv(h, s, v, Math::round(scroll[3]->get_value() / 255.0));
- }
+ color = modes[current_mode]->get_color();
+ if (current_mode == MODE_HSV || current_mode == MODE_OKHSL) {
+ h = sliders[0]->get_value() / 360.0;
+ s = sliders[1]->get_value() / 100.0;
+ v = sliders[2]->get_value() / 100.0;
last_color = color;
- } else {
- for (int i = 0; i < 4; i++) {
- color.components[i] = scroll[i]->get_value() / (raw_mode_enabled ? 1.0 : 255.0);
- }
}
_set_pick_color(color, false);
emit_signal(SNAME("color_changed"), color);
}
+void ColorPicker::add_mode(ColorMode *p_mode) {
+ modes.push_back(p_mode);
+ mode_option_button->add_item(RTR(p_mode->get_name()));
+}
+
+void ColorPicker::create_slider(GridContainer *gc, int idx) {
+ Label *l = memnew(Label());
+ l->set_v_size_flags(SIZE_SHRINK_CENTER);
+ gc->add_child(l);
+
+ HSlider *s = memnew(HSlider);
+ s->set_v_size_flags(SIZE_SHRINK_CENTER);
+ s->set_focus_mode(FOCUS_NONE);
+ gc->add_child(s);
+
+ SpinBox *v = memnew(SpinBox);
+ s->share(v);
+ gc->add_child(v);
+ v->get_line_edit()->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter));
+ v->get_line_edit()->connect("focus_exited", callable_mp(this, &ColorPicker::_focus_exit));
+
+ s->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ s->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed));
+ s->connect("draw", callable_mp(this, &ColorPicker::_slider_draw), make_binds(idx));
+
+ if (idx < SLIDER_COUNT) {
+ sliders[idx] = s;
+ values[idx] = v;
+ labels[idx] = l;
+ } else {
+ alpha_slider = s;
+ alpha_value = v;
+ alpha_label = l;
+ }
+}
+
+HSlider *ColorPicker::get_slider(int idx) {
+ if (idx < SLIDER_COUNT) {
+ return sliders[idx];
+ }
+ return alpha_slider;
+}
+
+Vector<float> ColorPicker::get_active_slider_values() {
+ Vector<float> values;
+ for (int i = 0; i < current_slider_count; i++) {
+ values.push_back(sliders[i]->get_value());
+ }
+ values.push_back(alpha_slider->get_value());
+ return values;
+}
+
+ColorPicker::PickerShapeType ColorPicker::_get_actual_shape() const {
+ return modes[current_mode]->get_shape_override() != SHAPE_MAX ? modes[current_mode]->get_shape_override() : current_shape;
+}
+
+void ColorPicker::_reset_theme() {
+ Ref<StyleBoxEmpty> style_box_empty(memnew(StyleBoxEmpty));
+
+ for (int i = 0; i < SLIDER_COUNT; i++) {
+ sliders[i]->add_theme_icon_override("grabber", get_theme_icon(SNAME("bar_arrow"), SNAME("ColorPicker")));
+ sliders[i]->add_theme_icon_override("grabber_highlight", get_theme_icon(SNAME("bar_arrow"), SNAME("ColorPicker")));
+ sliders[i]->add_theme_style_override("slider", style_box_empty);
+ sliders[i]->add_theme_style_override("grabber_area", style_box_empty);
+ sliders[i]->add_theme_style_override("grabber_area_highlight", style_box_empty);
+ }
+ alpha_slider->add_theme_icon_override("grabber", get_theme_icon(SNAME("bar_arrow"), SNAME("ColorPicker")));
+ alpha_slider->add_theme_icon_override("grabber_highlight", get_theme_icon(SNAME("bar_arrow"), SNAME("ColorPicker")));
+ alpha_slider->add_theme_style_override("slider", style_box_empty);
+ alpha_slider->add_theme_style_override("grabber_area", style_box_empty);
+ alpha_slider->add_theme_style_override("grabber_area_highlight", style_box_empty);
+}
+
void ColorPicker::_html_submitted(const String &p_html) {
if (updating || text_is_constructor || !c_text->is_visible()) {
return;
@@ -397,35 +430,15 @@ void ColorPicker::_update_color(bool p_update_sliders) {
updating = true;
if (p_update_sliders) {
- if (hsv_mode_enabled) {
- for (int i = 0; i < 4; i++) {
- scroll[i]->set_step(1.0);
- }
- scroll[0]->set_max(359);
- scroll[0]->set_value(h * 360.0);
- scroll[1]->set_max(100);
- scroll[1]->set_value(s * 100.0);
- scroll[2]->set_max(100);
- scroll[2]->set_value(v * 100.0);
- scroll[3]->set_max(255);
- scroll[3]->set_value(Math::round(color.components[3] * 255.0));
- } else {
- for (int i = 0; i < 4; i++) {
- if (raw_mode_enabled) {
- scroll[i]->set_step(0.01);
- scroll[i]->set_max(100);
- if (i == 3) {
- scroll[i]->set_max(1);
- }
- scroll[i]->set_value(color.components[i]);
- } else {
- scroll[i]->set_step(1);
- const float byte_value = Math::round(color.components[i] * 255.0);
- scroll[i]->set_max(next_power_of_2(MAX(255, byte_value)) - 1);
- scroll[i]->set_value(byte_value);
- }
- }
+ float step = modes[current_mode]->get_slider_step();
+ for (int i = 0; i < current_slider_count; i++) {
+ sliders[i]->set_max(modes[current_mode]->get_slider_max(i));
+ sliders[i]->set_step(step);
+ sliders[i]->set_value(modes[current_mode]->get_slider_value(i));
}
+ alpha_slider->set_max(modes[current_mode]->get_slider_max(current_slider_count));
+ alpha_slider->set_step(step);
+ alpha_slider->set_value(modes[current_mode]->get_slider_value(current_slider_count));
}
_update_text_value();
@@ -433,9 +446,10 @@ void ColorPicker::_update_color(bool p_update_sliders) {
sample->update();
uv_edit->update();
w_edit->update();
- for (int i = 0; i < 4; i++) {
- scroll[i]->update();
+ for (int i = 0; i < current_slider_count; i++) {
+ sliders[i]->update();
}
+ alpha_slider->update();
wheel->update();
wheel_uv->update();
updating = false;
@@ -481,15 +495,16 @@ Color ColorPicker::get_pick_color() const {
return color;
}
-void ColorPicker::set_picker_shape(PickerShapeType p_picker_type) {
- ERR_FAIL_INDEX(p_picker_type, SHAPE_MAX);
- picker_type = p_picker_type;
+void ColorPicker::set_picker_shape(PickerShapeType p_shape) {
+ ERR_FAIL_INDEX(p_shape, SHAPE_MAX);
+ current_shape = p_shape;
+
_update_controls();
_update_color();
}
ColorPicker::PickerShapeType ColorPicker::get_picker_shape() const {
- return picker_type;
+ return current_shape;
}
inline int ColorPicker::_get_preset_size() {
@@ -505,6 +520,21 @@ void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
preset_container->add_child(btn_preset);
}
+void ColorPicker::_set_color_mode(ColorModeType p_mode) {
+ if (slider_theme_modified) {
+ _reset_theme();
+ }
+
+ current_mode = p_mode;
+
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ _update_controls();
+ _update_color();
+}
+
void ColorPicker::add_preset(const Color &p_color) {
if (presets.find(p_color)) {
presets.move_to_back(presets.find(p_color));
@@ -564,46 +594,14 @@ PackedColorArray ColorPicker::get_presets() const {
return arr;
}
-void ColorPicker::set_hsv_mode(bool p_enabled) {
- if (hsv_mode_enabled == p_enabled || raw_mode_enabled) {
- return;
- }
- hsv_mode_enabled = p_enabled;
- if (btn_hsv->is_pressed() != p_enabled) {
- btn_hsv->set_pressed(p_enabled);
- }
-
- if (!is_inside_tree()) {
- return;
- }
-
- _update_controls();
- _update_color();
-}
-
-bool ColorPicker::is_hsv_mode() const {
- return hsv_mode_enabled;
-}
-
-void ColorPicker::set_raw_mode(bool p_enabled) {
- if (raw_mode_enabled == p_enabled || hsv_mode_enabled) {
- return;
- }
- raw_mode_enabled = p_enabled;
- if (btn_raw->is_pressed() != p_enabled) {
- btn_raw->set_pressed(p_enabled);
- }
-
- if (!is_inside_tree()) {
- return;
- }
-
- _update_controls();
- _update_color();
+void ColorPicker::set_color_mode(ColorModeType p_mode) {
+ ERR_FAIL_INDEX(p_mode, MODE_MAX);
+ mode_option_button->select(p_mode);
+ _set_color_mode(p_mode);
}
-bool ColorPicker::is_raw_mode() const {
- return raw_mode_enabled;
+ColorPicker::ColorModeType ColorPicker::get_color_mode() const {
+ return current_mode;
}
void ColorPicker::set_deferred_mode(bool p_enabled) {
@@ -690,6 +688,8 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
if (!c) {
return;
}
+
+ PickerShapeType actual_shape = _get_actual_shape();
if (p_which == 0) {
Vector<Point2> points;
Vector<Color> colors;
@@ -697,7 +697,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
Color col = color;
Vector2 center = c->get_size() / 2.0;
- switch (picker_type) {
+ switch (actual_shape) {
case SHAPE_HSV_WHEEL: {
points.resize(4);
colors.resize(4);
@@ -759,7 +759,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
Ref<Texture2D> cursor = get_theme_icon(SNAME("picker_cursor"), SNAME("ColorPicker"));
int x;
int y;
- if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) {
+ if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
x = center.x + (center.x * Math::cos(h * Math_TAU) * s) - (cursor->get_width() / 2);
y = center.y + (center.y * Math::sin(h * Math_TAU) * s) - (cursor->get_height() / 2);
} else {
@@ -773,7 +773,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
c->draw_texture(cursor, Point2(x, y));
col.set_hsv(h, 1, 1);
- if (picker_type == SHAPE_HSV_WHEEL) {
+ if (actual_shape == SHAPE_HSV_WHEEL) {
points.resize(4);
double h1 = h - (0.5 / 360);
double h2 = h + (0.5 / 360);
@@ -785,14 +785,14 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
}
} else if (p_which == 1) {
- if (picker_type == SHAPE_HSV_RECTANGLE) {
+ if (actual_shape == SHAPE_HSV_RECTANGLE) {
Ref<Texture2D> hue = get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
c->draw_texture_rect(hue, Rect2(Point2(), c->get_size()));
int y = c->get_size().y - c->get_size().y * (1.0 - h);
Color col;
col.set_hsv(h, 1, 1);
c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted());
- } else if (picker_type == SHAPE_OKHSL_CIRCLE) {
+ } else if (actual_shape == SHAPE_OKHSL_CIRCLE) {
Vector<Point2> points;
Vector<Color> colors;
Color col;
@@ -811,7 +811,7 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
int y = c->get_size().y - c->get_size().y * CLAMP(v, 0, 1);
col.set_ok_hsl(h, 1, v);
c->draw_line(Point2(0, y), Point2(c->get_size().x, y), col.inverted());
- } else if (picker_type == SHAPE_VHS_CIRCLE) {
+ } else if (actual_shape == SHAPE_VHS_CIRCLE) {
Vector<Point2> points;
Vector<Color> colors;
Color col;
@@ -833,87 +833,24 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) {
}
} else if (p_which == 2) {
c->draw_rect(Rect2(Point2(), c->get_size()), Color(1, 1, 1));
- if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) {
+ if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
circle_mat->set_shader_param("v", v);
}
}
}
void ColorPicker::_slider_draw(int p_which) {
- Vector<Vector2> pos;
- pos.resize(4);
- Vector<Color> col;
- col.resize(4);
- Size2 size = scroll[p_which]->get_size();
- Color left_color;
- Color right_color;
- const real_t margin = 4 * get_theme_default_base_scale();
-
- if (p_which == 3) {
- 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;
- right_color = color;
- right_color.a = 1;
- } else {
- if (raw_mode_enabled) {
- return;
- }
- if (hsv_mode_enabled) {
- if (p_which == 0) {
- Ref<Texture2D> hue = get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker"));
- scroll[p_which]->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0));
- scroll[p_which]->draw_texture_rect(hue, Rect2(Vector2(margin * -2, 0), Vector2(scroll[p_which]->get_size().x, margin)), false, Color(1, 1, 1), true);
- return;
- }
- Color s_col;
- Color v_col;
- if (picker_type == SHAPE_OKHSL_CIRCLE) {
- s_col.set_ok_hsl(h, 0, v);
- } else {
- s_col.set_hsv(h, 0, v);
- }
- left_color = (p_which == 1) ? s_col : Color(0, 0, 0);
- if (picker_type == SHAPE_OKHSL_CIRCLE) {
- s_col.set_ok_hsl(h, 1, v);
- v_col.set_ok_hsl(h, s, 1);
- } else {
- s_col.set_hsv(h, 1, v);
- v_col.set_hsv(h, s, 1);
- }
- right_color = (p_which == 1) ? s_col : v_col;
- } 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);
- }
- }
-
- 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, margin));
- pos.set(1, Vector2(size.x, margin));
- pos.set(2, Vector2(size.x, margin * 2));
- pos.set(3, Vector2(0, margin * 2));
-
- scroll[p_which]->draw_polygon(pos, col);
+ modes[current_mode]->slider_draw(p_which);
}
void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
Ref<InputEventMouseButton> bev = p_event;
+ PickerShapeType current_picker = _get_actual_shape();
if (bev.is_valid()) {
if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
Vector2 center = c->get_size() / 2.0;
- if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) {
+ if (current_picker == SHAPE_VHS_CIRCLE || current_picker == SHAPE_OKHSL_CIRCLE) {
real_t dist = center.distance_to(bev->get_position());
if (dist <= center.x) {
real_t rad = center.angle_to_point(bev->get_position());
@@ -951,11 +888,12 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
}
}
changing_color = true;
- if (picker_type == SHAPE_OKHSL_CIRCLE) {
+ if (current_picker == SHAPE_OKHSL_CIRCLE) {
color.set_ok_hsl(h, s, v, color.a);
- } else if (picker_type != SHAPE_OKHSL_CIRCLE) {
+ } else {
color.set_hsv(h, s, v, color.a);
}
+
last_color = color;
set_pick_color(color);
@@ -981,7 +919,7 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
}
Vector2 center = c->get_size() / 2.0;
- if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) {
+ if (current_picker == SHAPE_VHS_CIRCLE || current_picker == SHAPE_OKHSL_CIRCLE) {
real_t dist = center.distance_to(mev->get_position());
real_t rad = center.angle_to_point(mev->get_position());
h = ((rad >= 0) ? rad : (Math_TAU + rad)) / Math_TAU;
@@ -1002,9 +940,9 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
v = 1.0 - (y - corner_y) / real_size.y;
}
}
- if (picker_type != SHAPE_OKHSL_CIRCLE) {
+ if (current_picker != SHAPE_OKHSL_CIRCLE) {
color.set_hsv(h, s, v, color.a);
- } else if (picker_type == SHAPE_OKHSL_CIRCLE) {
+ } else {
color.set_ok_hsl(h, s, v, color.a);
}
last_color = color;
@@ -1018,12 +956,13 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> bev = p_event;
+ PickerShapeType actual_shape = _get_actual_shape();
if (bev.is_valid()) {
if (bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) {
changing_color = true;
float y = CLAMP((float)bev->get_position().y, 0, w_edit->get_size().height);
- if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) {
+ if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
v = 1.0 - (y / w_edit->get_size().height);
} else {
h = y / w_edit->get_size().height;
@@ -1031,9 +970,9 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
} else {
changing_color = false;
}
- if (picker_type != SHAPE_OKHSL_CIRCLE) {
+ if (actual_shape != SHAPE_OKHSL_CIRCLE) {
color.set_hsv(h, s, v, color.a);
- } else if (picker_type == SHAPE_OKHSL_CIRCLE) {
+ } else {
color.set_ok_hsl(h, s, v, color.a);
}
last_color = color;
@@ -1053,16 +992,18 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) {
return;
}
float y = CLAMP((float)mev->get_position().y, 0, w_edit->get_size().height);
- if (picker_type == SHAPE_VHS_CIRCLE || picker_type == SHAPE_OKHSL_CIRCLE) {
+ if (actual_shape == SHAPE_VHS_CIRCLE || actual_shape == SHAPE_OKHSL_CIRCLE) {
v = 1.0 - (y / w_edit->get_size().height);
} else {
h = y / w_edit->get_size().height;
}
- if (hsv_mode_enabled && picker_type != SHAPE_OKHSL_CIRCLE) {
+
+ if (current_mode == MODE_HSV) {
color.set_hsv(h, s, v, color.a);
- } else if (hsv_mode_enabled && picker_type == SHAPE_OKHSL_CIRCLE) {
+ } else if (current_mode == MODE_OKHSL) {
color.set_ok_hsl(h, s, v, color.a);
}
+
last_color = color;
set_pick_color(color);
_update_color();
@@ -1153,21 +1094,30 @@ void ColorPicker::_focus_enter() {
c_text->select(0, 0);
}
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < current_slider_count; i++) {
if (values[i]->get_line_edit()->has_focus() && !has_ctext_focus) {
values[i]->get_line_edit()->select_all();
} else {
values[i]->get_line_edit()->select(0, 0);
}
}
+ if (alpha_value->get_line_edit()->has_focus() && !has_ctext_focus) {
+ alpha_value->get_line_edit()->select_all();
+ } else {
+ alpha_value->get_line_edit()->select(0, 0);
+ }
}
void ColorPicker::_focus_exit() {
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < current_slider_count; i++) {
if (!values[i]->get_line_edit()->get_menu()->is_visible()) {
values[i]->get_line_edit()->select(0, 0);
}
}
+ if (!alpha_value->get_line_edit()->get_menu()->is_visible()) {
+ alpha_value->get_line_edit()->select(0, 0);
+ }
+
c_text->select(0, 0);
}
@@ -1207,12 +1157,10 @@ bool ColorPicker::are_presets_visible() const {
void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color);
ClassDB::bind_method(D_METHOD("get_pick_color"), &ColorPicker::get_pick_color);
- ClassDB::bind_method(D_METHOD("set_hsv_mode", "enabled"), &ColorPicker::set_hsv_mode);
- ClassDB::bind_method(D_METHOD("is_hsv_mode"), &ColorPicker::is_hsv_mode);
- ClassDB::bind_method(D_METHOD("set_raw_mode", "enabled"), &ColorPicker::set_raw_mode);
- ClassDB::bind_method(D_METHOD("is_raw_mode"), &ColorPicker::is_raw_mode);
ClassDB::bind_method(D_METHOD("set_deferred_mode", "mode"), &ColorPicker::set_deferred_mode);
ClassDB::bind_method(D_METHOD("is_deferred_mode"), &ColorPicker::is_deferred_mode);
+ ClassDB::bind_method(D_METHOD("set_color_mode", "color_mode"), &ColorPicker::set_color_mode);
+ 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_presets_enabled", "enabled"), &ColorPicker::set_presets_enabled);
@@ -1222,13 +1170,12 @@ void ColorPicker::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_preset", "color"), &ColorPicker::add_preset);
ClassDB::bind_method(D_METHOD("erase_preset", "color"), &ColorPicker::erase_preset);
ClassDB::bind_method(D_METHOD("get_presets"), &ColorPicker::get_presets);
- ClassDB::bind_method(D_METHOD("set_picker_shape", "picker"), &ColorPicker::set_picker_shape);
+ ClassDB::bind_method(D_METHOD("set_picker_shape", "shape"), &ColorPicker::set_picker_shape);
ClassDB::bind_method(D_METHOD("get_picker_shape"), &ColorPicker::get_picker_shape);
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, "hsv_mode"), "set_hsv_mode", "is_hsv_mode");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "raw_mode"), "set_raw_mode", "is_raw_mode");
+ 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, "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"), "set_picker_shape", "get_picker_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "presets_enabled"), "set_presets_enabled", "are_presets_enabled");
@@ -1238,6 +1185,11 @@ void ColorPicker::_bind_methods() {
ADD_SIGNAL(MethodInfo("preset_added", PropertyInfo(Variant::COLOR, "color")));
ADD_SIGNAL(MethodInfo("preset_removed", PropertyInfo(Variant::COLOR, "color")));
+ BIND_ENUM_CONSTANT(MODE_RGB);
+ BIND_ENUM_CONSTANT(MODE_HSV);
+ BIND_ENUM_CONSTANT(MODE_RAW);
+ BIND_ENUM_CONSTANT(MODE_OKHSL);
+
BIND_ENUM_CONSTANT(SHAPE_HSV_RECTANGLE);
BIND_ENUM_CONSTANT(SHAPE_HSV_WHEEL);
BIND_ENUM_CONSTANT(SHAPE_VHS_CIRCLE);
@@ -1250,6 +1202,7 @@ ColorPicker::ColorPicker() :
add_child(hb_edit, false, INTERNAL_MODE_FRONT);
hb_edit->set_v_size_flags(SIZE_EXPAND_FILL);
+ uv_edit = memnew(Control);
hb_edit->add_child(uv_edit);
uv_edit->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(uv_edit));
uv_edit->set_mouse_filter(MOUSE_FILTER_PASS);
@@ -1260,11 +1213,13 @@ ColorPicker::ColorPicker() :
HBoxContainer *hb_smpl = memnew(HBoxContainer);
add_child(hb_smpl, false, INTERNAL_MODE_FRONT);
+ sample = memnew(TextureRect);
hb_smpl->add_child(sample);
sample->set_h_size_flags(SIZE_EXPAND_FILL);
sample->connect("gui_input", callable_mp(this, &ColorPicker::_sample_input));
sample->connect("draw", callable_mp(this, &ColorPicker::_sample_draw));
+ btn_pick = memnew(Button);
btn_pick->set_flat(true);
hb_smpl->add_child(btn_pick);
btn_pick->set_toggle_mode(true);
@@ -1277,51 +1232,35 @@ ColorPicker::ColorPicker() :
add_child(memnew(HSeparator), false, INTERNAL_MODE_FRONT);
VBoxContainer *vbr = memnew(VBoxContainer);
+
add_child(vbr, false, INTERNAL_MODE_FRONT);
vbr->set_h_size_flags(SIZE_EXPAND_FILL);
- for (int i = 0; i < 4; i++) {
- HBoxContainer *hbc = memnew(HBoxContainer);
+ GridContainer *gc = memnew(GridContainer);
- labels[i] = memnew(Label());
- labels[i]->set_custom_minimum_size(Size2(get_theme_constant(SNAME("label_width")), 0));
- labels[i]->set_v_size_flags(SIZE_SHRINK_CENTER);
- hbc->add_child(labels[i]);
+ vbr->add_child(gc);
+ gc->set_h_size_flags(SIZE_EXPAND_FILL);
+ gc->set_columns(3);
- scroll[i] = memnew(HSlider);
- scroll[i]->set_v_size_flags(SIZE_SHRINK_CENTER);
- scroll[i]->set_focus_mode(FOCUS_NONE);
- hbc->add_child(scroll[i]);
-
- values[i] = memnew(SpinBox);
- scroll[i]->share(values[i]);
- hbc->add_child(values[i]);
- values[i]->get_line_edit()->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter));
- values[i]->get_line_edit()->connect("focus_exited", callable_mp(this, &ColorPicker::_focus_exit));
-
- scroll[i]->set_min(0);
- scroll[i]->set_page(0);
- scroll[i]->set_h_size_flags(SIZE_EXPAND_FILL);
-
- scroll[i]->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed));
- scroll[i]->connect("draw", callable_mp(this, &ColorPicker::_slider_draw), make_binds(i));
-
- vbr->add_child(hbc);
+ for (int i = 0; i < SLIDER_COUNT + 1; i++) {
+ create_slider(gc, i);
}
- labels[3]->set_text("A");
+ alpha_label->set_text("A");
HBoxContainer *hhb = memnew(HBoxContainer);
vbr->add_child(hhb);
- hhb->add_child(btn_hsv);
- btn_hsv->set_text(RTR("HSV"));
- btn_hsv->connect("toggled", callable_mp(this, &ColorPicker::set_hsv_mode));
+ mode_option_button = memnew(OptionButton);
- hhb->add_child(btn_raw);
- btn_raw->set_text(RTR("Raw"));
- btn_raw->connect("toggled", callable_mp(this, &ColorPicker::set_raw_mode));
+ hhb->add_child(mode_option_button);
+ add_mode(new ColorModeRGB(this));
+ add_mode(new ColorModeHSV(this));
+ add_mode(new ColorModeRAW(this));
+ add_mode(new ColorModeOKHSL(this));
+ mode_option_button->connect("item_selected", callable_mp(this, &ColorPicker::_set_color_mode));
+ text_type = memnew(Button);
hhb->add_child(text_type);
text_type->set_text("#");
text_type->set_tooltip(RTR("Switch between hexadecimal and code values."));
@@ -1332,12 +1271,14 @@ ColorPicker::ColorPicker() :
text_type->set_mouse_filter(MOUSE_FILTER_IGNORE);
}
+ c_text = memnew(LineEdit);
hhb->add_child(c_text);
c_text->set_h_size_flags(SIZE_EXPAND_FILL);
c_text->connect("text_submitted", callable_mp(this, &ColorPicker::_html_submitted));
c_text->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter));
c_text->connect("focus_exited", callable_mp(this, &ColorPicker::_html_focus_exit));
+ wheel_edit = memnew(AspectRatioContainer);
wheel_edit->set_h_size_flags(SIZE_EXPAND_FILL);
wheel_edit->set_v_size_flags(SIZE_EXPAND_FILL);
hb_edit->add_child(wheel_edit);
@@ -1347,41 +1288,53 @@ ColorPicker::ColorPicker() :
circle_mat.instantiate();
circle_mat->set_shader(circle_shader);
+ wheel_margin = memnew(MarginContainer);
wheel_margin->add_theme_constant_override("margin_bottom", 8);
wheel_edit->add_child(wheel_margin);
+ wheel = memnew(Control);
wheel_margin->add_child(wheel);
wheel->set_mouse_filter(MOUSE_FILTER_PASS);
wheel->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(2, wheel));
+ wheel_uv = memnew(Control);
wheel_margin->add_child(wheel_uv);
wheel_uv->connect("gui_input", callable_mp(this, &ColorPicker::_uv_input), make_binds(wheel_uv));
wheel_uv->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(0, wheel_uv));
+ w_edit = memnew(Control);
hb_edit->add_child(w_edit);
w_edit->set_h_size_flags(SIZE_FILL);
w_edit->set_v_size_flags(SIZE_EXPAND_FILL);
w_edit->connect("gui_input", callable_mp(this, &ColorPicker::_w_input));
w_edit->connect("draw", callable_mp(this, &ColorPicker::_hsv_draw), make_binds(1, w_edit));
- picker_type = SHAPE_HSV_RECTANGLE;
_update_controls();
updating = false;
set_pick_color(Color(1, 1, 1));
+ preset_separator = memnew(HSeparator);
add_child(preset_separator, false, INTERNAL_MODE_FRONT);
+ preset_container = memnew(GridContainer);
preset_container->set_h_size_flags(SIZE_EXPAND_FILL);
preset_container->set_columns(preset_column_count);
add_child(preset_container, false, INTERNAL_MODE_FRONT);
+ btn_add_preset = memnew(Button);
btn_add_preset->set_icon_alignment(HORIZONTAL_ALIGNMENT_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);
}
+ColorPicker::~ColorPicker() {
+ for (int i = 0; i < modes.size(); i++) {
+ delete modes[i];
+ }
+}
+
/////////////////
void ColorPickerButton::_about_to_popup() {
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 953be032ec..e219c78319 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -34,16 +34,23 @@
#include "scene/gui/aspect_ratio_container.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
-#include "scene/gui/check_button.h"
+#include "scene/gui/control.h"
#include "scene/gui/grid_container.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
+#include "scene/gui/option_button.h"
#include "scene/gui/popup.h"
#include "scene/gui/separator.h"
#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/texture_rect.h"
+class ColorMode;
+class ColorModeRGB;
+class ColorModeHSV;
+class ColorModeRAW;
+class ColorModeOKHSL;
+
class ColorPresetButton : public BaseButton {
GDCLASS(ColorPresetButton, BaseButton);
@@ -64,6 +71,15 @@ class ColorPicker : public BoxContainer {
GDCLASS(ColorPicker, BoxContainer);
public:
+ enum ColorModeType {
+ MODE_RGB,
+ MODE_HSV,
+ MODE_RAW,
+ MODE_OKHSL,
+
+ MODE_MAX
+ };
+
enum PickerShapeType {
SHAPE_HSV_RECTANGLE,
SHAPE_HSV_WHEEL,
@@ -73,38 +89,52 @@ public:
SHAPE_MAX
};
+ static const int SLIDER_COUNT = 4;
+
private:
static Ref<Shader> wheel_shader;
static Ref<Shader> circle_shader;
static Ref<Shader> circle_ok_color_shader;
static List<Color> preset_cache;
+ int current_slider_count = SLIDER_COUNT;
+
+ bool slider_theme_modified = true;
+
+ Vector<ColorMode *> modes;
+
Control *screen = nullptr;
- Control *uv_edit = memnew(Control);
- Control *w_edit = memnew(Control);
- AspectRatioContainer *wheel_edit = memnew(AspectRatioContainer);
- MarginContainer *wheel_margin = memnew(MarginContainer);
+ Control *uv_edit = nullptr;
+ Control *w_edit = nullptr;
+ AspectRatioContainer *wheel_edit = nullptr;
+ MarginContainer *wheel_margin = nullptr;
Ref<ShaderMaterial> wheel_mat;
Ref<ShaderMaterial> circle_mat;
- Control *wheel = memnew(Control);
- Control *wheel_uv = memnew(Control);
- TextureRect *sample = memnew(TextureRect);
- GridContainer *preset_container = memnew(GridContainer);
- HSeparator *preset_separator = memnew(HSeparator);
- Button *btn_add_preset = memnew(Button);
- Button *btn_pick = memnew(Button);
- CheckButton *btn_hsv = memnew(CheckButton);
- CheckButton *btn_raw = memnew(CheckButton);
- HSlider *scroll[4];
- SpinBox *values[4];
- Label *labels[4];
- Button *text_type = memnew(Button);
- LineEdit *c_text = memnew(LineEdit);
+ Control *wheel = nullptr;
+ Control *wheel_uv = nullptr;
+ TextureRect *sample = nullptr;
+ GridContainer *preset_container = nullptr;
+ HSeparator *preset_separator = nullptr;
+ Button *btn_add_preset = nullptr;
+ Button *btn_pick = nullptr;
+
+ OptionButton *mode_option_button = nullptr;
+
+ HSlider *sliders[SLIDER_COUNT];
+ SpinBox *values[SLIDER_COUNT];
+ Label *labels[SLIDER_COUNT];
+ Button *text_type = nullptr;
+ LineEdit *c_text = nullptr;
+
+ HSlider *alpha_slider = nullptr;
+ SpinBox *alpha_value = nullptr;
+ Label *alpha_label = nullptr;
bool edit_alpha = true;
Size2i ms;
bool text_is_constructor = false;
- PickerShapeType picker_type = SHAPE_HSV_WHEEL;
+ PickerShapeType current_shape = SHAPE_HSV_RECTANGLE;
+ ColorModeType current_mode = MODE_RGB;
const int preset_column_count = 9;
int prev_preset_size = 0;
@@ -114,8 +144,6 @@ private:
Color old_color;
bool display_old_color = false;
- bool raw_mode_enabled = false;
- bool hsv_mode_enabled = false;
bool deferred_mode_enabled = false;
bool updating = true;
bool changing_color = false;
@@ -128,6 +156,9 @@ private:
float v = 0.0;
Color last_color;
+ PickerShapeType _get_actual_shape() const;
+ void create_slider(GridContainer *gc, int idx);
+ void _reset_theme();
void _html_submitted(const String &p_html);
void _value_changed(double);
void _update_controls();
@@ -152,14 +183,21 @@ private:
inline int _get_preset_size();
void _add_preset_button(int p_size, const Color &p_color);
+ void _set_color_mode(ColorModeType p_mode);
+
protected:
void _notification(int);
static void _bind_methods();
public:
+ HSlider *get_slider(int idx);
+ Vector<float> get_active_slider_values();
+
static void init_shaders();
static void finish_shaders();
+ void add_mode(ColorMode *p_mode);
+
void set_edit_alpha(bool p_show);
bool is_editing_alpha() const;
@@ -173,7 +211,7 @@ public:
void set_display_old_color(bool p_enabled);
bool is_displaying_old_color() const;
- void set_picker_shape(PickerShapeType p_picker_type);
+ void set_picker_shape(PickerShapeType p_shape);
PickerShapeType get_picker_shape() const;
void add_preset(const Color &p_color);
@@ -181,11 +219,8 @@ public:
PackedColorArray get_presets() const;
void _update_presets();
- void set_hsv_mode(bool p_enabled);
- bool is_hsv_mode() const;
-
- void set_raw_mode(bool p_enabled);
- bool is_raw_mode() const;
+ void set_color_mode(ColorModeType p_mode);
+ ColorModeType get_color_mode() const;
void set_deferred_mode(bool p_enabled);
bool is_deferred_mode() const;
@@ -199,6 +234,7 @@ public:
void set_focus_on_line_edit();
ColorPicker();
+ ~ColorPicker();
};
class ColorPickerButton : public Button {
@@ -239,4 +275,5 @@ public:
};
VARIANT_ENUM_CAST(ColorPicker::PickerShapeType);
+VARIANT_ENUM_CAST(ColorPicker::ColorModeType);
#endif // COLOR_PICKER_H
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 7b97b0fa47..15ff1d3ed6 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -723,8 +723,20 @@ void Control::_notification(int p_notification) {
data.parent_window = Object::cast_to<Window>(get_parent());
data.is_rtl_dirty = true;
+ if (data.theme.is_null()) {
+ if (data.parent && (data.parent->data.theme_owner || data.parent->data.theme_owner_window)) {
+ data.theme_owner = data.parent->data.theme_owner;
+ data.theme_owner_window = data.parent->data.theme_owner_window;
+ notification(NOTIFICATION_THEME_CHANGED);
+ } else if (data.parent_window && (data.parent_window->theme_owner || data.parent_window->theme_owner_window)) {
+ data.theme_owner = data.parent_window->theme_owner;
+ data.theme_owner_window = data.parent_window->theme_owner_window;
+ notification(NOTIFICATION_THEME_CHANGED);
+ }
+ }
+
CanvasItem *node = this;
- Control *parent_control = nullptr;
+ bool has_parent_control = false;
while (!node->is_set_as_top_level()) {
CanvasItem *parent = Object::cast_to<CanvasItem>(node->get_parent());
@@ -732,22 +744,19 @@ void Control::_notification(int p_notification) {
break;
}
- parent_control = Object::cast_to<Control>(parent);
+ Control *parent_control = Object::cast_to<Control>(parent);
if (parent_control) {
+ has_parent_control = true;
break;
}
node = parent;
}
- if (parent_control) {
+ if (has_parent_control) {
// Do nothing, has a parent control.
- if (data.theme.is_null() && parent_control->data.theme_owner) {
- data.theme_owner = parent_control->data.theme_owner;
- notification(NOTIFICATION_THEME_CHANGED);
- }
} else {
- //is a regular root control or top_level
+ // Is a regular root control or top_level.
Viewport *viewport = get_viewport();
ERR_FAIL_COND(!viewport);
data.RI = viewport->_gui_add_root_control(this);
@@ -758,7 +767,7 @@ void Control::_notification(int p_notification) {
if (data.parent_canvas_item) {
data.parent_canvas_item->connect("item_rect_changed", callable_mp(this, &Control::_size_changed));
} else {
- //connect viewport
+ // Connect viewport.
Viewport *viewport = get_viewport();
ERR_FAIL_COND(!viewport);
viewport->connect("size_changed", callable_mp(this, &Control::_size_changed));
@@ -1106,7 +1115,7 @@ Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const String
Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
const Ref<Font> *font = data.font_override.getptr(p_name);
- if (font && (*font)->get_data_count() > 0) {
+ if (font) {
return *font;
}
}
@@ -2335,7 +2344,7 @@ void Control::set_focus_mode(FocusMode p_focus_mode) {
static Control *_next_control(Control *p_from) {
if (p_from->is_set_as_top_level()) {
- return nullptr; // can't go above
+ return nullptr; // Can't go above.
}
Control *parent = Object::cast_to<Control>(p_from->get_parent());
@@ -2355,7 +2364,7 @@ static Control *_next_control(Control *p_from) {
return c;
}
- //no next in parent, try the same in parent
+ // No next in parent, try the same in parent.
return _next_control(parent);
}
@@ -2379,7 +2388,7 @@ Control *Control::find_next_valid_focus() const {
}
}
- // find next child
+ // Find next child.
Control *next_child = nullptr;
@@ -2395,7 +2404,7 @@ Control *Control::find_next_valid_focus() const {
if (!next_child) {
next_child = _next_control(from);
- if (!next_child) { //nothing else.. go up and find either window or subwindow
+ if (!next_child) { // Nothing else. Go up and find either window or subwindow.
next_child = const_cast<Control *>(this);
while (next_child && !next_child->is_set_as_top_level()) {
next_child = cast_to<Control>(next_child->get_parent());
@@ -2413,7 +2422,7 @@ Control *Control::find_next_valid_focus() const {
}
}
- if (next_child == this) { // no next control->
+ if (next_child == from || next_child == this) { // No next control.
return (get_focus_mode() == FOCUS_ALL) ? next_child : nullptr;
}
if (next_child) {
@@ -2445,7 +2454,7 @@ static Control *_prev_control(Control *p_from) {
return p_from;
}
- //no prev in parent, try the same in parent
+ // No prev in parent, try the same in parent.
return _prev_control(child);
}
@@ -2469,12 +2478,12 @@ Control *Control::find_prev_valid_focus() const {
}
}
- // find prev child
+ // Find prev child.
Control *prev_child = nullptr;
if (from->is_set_as_top_level() || !Object::cast_to<Control>(from->get_parent())) {
- //find last of the children
+ // Find last of the children.
prev_child = _prev_control(from);
@@ -2497,7 +2506,7 @@ Control *Control::find_prev_valid_focus() const {
}
}
- if (prev_child == this) { // no prev control->
+ if (prev_child == from || prev_child == this) { // No prev control.
return (get_focus_mode() == FOCUS_ALL) ? prev_child : nullptr;
}
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 0e0f8bde83..6bb4ac9c6f 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -170,7 +170,11 @@ Vector<String> FileDialog::get_selected_files() const {
};
void FileDialog::update_dir() {
- dir->set_text(dir_access->get_current_dir(false));
+ if (root_prefix.is_empty()) {
+ dir->set_text(dir_access->get_current_dir(false));
+ } else {
+ dir->set_text(dir_access->get_current_dir(false).trim_prefix(root_prefix).trim_prefix("/"));
+ }
if (drives->is_visible()) {
if (dir_access->get_current_dir().is_network_share_path()) {
@@ -188,10 +192,8 @@ void FileDialog::update_dir() {
}
void FileDialog::_dir_submitted(String p_dir) {
- dir_access->change_dir(p_dir);
+ _change_dir(root_prefix.plus_file(p_dir));
file->set_text("");
- invalidate();
- update_dir();
_push_history();
}
@@ -378,9 +380,7 @@ bool FileDialog::_is_open_should_be_disabled() {
}
void FileDialog::_go_up() {
- dir_access->change_dir("..");
- update_file_list();
- update_dir();
+ _change_dir("..");
_push_history();
}
@@ -390,9 +390,7 @@ void FileDialog::_go_back() {
}
local_history_pos--;
- dir_access->change_dir(local_history[local_history_pos]);
- update_file_list();
- update_dir();
+ _change_dir(local_history[local_history_pos]);
dir_prev->set_disabled(local_history_pos == 0);
dir_next->set_disabled(local_history_pos == local_history.size() - 1);
@@ -404,9 +402,7 @@ void FileDialog::_go_forward() {
}
local_history_pos++;
- dir_access->change_dir(local_history[local_history_pos]);
- update_file_list();
- update_dir();
+ _change_dir(local_history[local_history_pos]);
dir_prev->set_disabled(local_history_pos == 0);
dir_next->set_disabled(local_history_pos == local_history.size() - 1);
@@ -465,12 +461,10 @@ void FileDialog::_tree_item_activated() {
Dictionary d = ti->get_metadata(0);
if (d["dir"]) {
- dir_access->change_dir(d["name"]);
+ _change_dir(d["name"]);
if (mode == FILE_MODE_OPEN_FILE || mode == FILE_MODE_OPEN_FILES || mode == FILE_MODE_OPEN_DIR || mode == FILE_MODE_OPEN_ANY) {
file->set_text("");
}
- call_deferred(SNAME("_update_file_list"));
- call_deferred(SNAME("_update_dir"));
_push_history();
} else {
_action_pressed();
@@ -486,7 +480,12 @@ void FileDialog::update_file_name() {
String filter_str = filters[idx];
String file_str = file->get_text();
String base_name = file_str.get_basename();
- file_str = base_name + "." + filter_str.strip_edges().to_lower();
+ Vector<String> filter_substr = filter_str.split(";");
+ if (filter_substr.size() >= 2) {
+ file_str = base_name + "." + filter_substr[0].strip_edges().get_extension().to_lower();
+ } else {
+ file_str = base_name + "." + filter_str.strip_edges().get_extension().to_lower();
+ }
file->set_text(file_str);
}
}
@@ -704,9 +703,7 @@ String FileDialog::get_current_path() const {
}
void FileDialog::set_current_dir(const String &p_dir) {
- dir_access->change_dir(p_dir);
- update_dir();
- invalidate();
+ _change_dir(p_dir);
_push_history();
}
@@ -732,6 +729,27 @@ void FileDialog::set_current_path(const String &p_path) {
}
}
+void FileDialog::set_root_subfolder(const String &p_root) {
+ root_subfolder = p_root;
+ ERR_FAIL_COND_MSG(!dir_access->dir_exists(p_root), "root_subfolder must be an existing sub-directory.");
+
+ local_history.clear();
+ local_history_pos = -1;
+
+ dir_access->change_dir(root_subfolder);
+ if (root_subfolder.is_empty()) {
+ root_prefix = "";
+ } else {
+ root_prefix = dir_access->get_current_dir();
+ }
+ invalidate();
+ update_dir();
+}
+
+String FileDialog::get_root_subfolder() const {
+ return root_subfolder;
+}
+
void FileDialog::set_mode_overrides_title(bool p_override) {
mode_overrides_title = p_override;
}
@@ -810,6 +828,8 @@ void FileDialog::set_access(Access p_access) {
} break;
}
access = p_access;
+ root_prefix = "";
+ root_subfolder = "";
_update_drives();
invalidate();
update_filters();
@@ -832,10 +852,8 @@ FileDialog::Access FileDialog::get_access() const {
void FileDialog::_make_dir_confirm() {
Error err = dir_access->make_dir(makedirname->get_text().strip_edges());
if (err == OK) {
- dir_access->change_dir(makedirname->get_text().strip_edges());
- invalidate();
+ _change_dir(makedirname->get_text().strip_edges());
update_filters();
- update_dir();
_push_history();
} else {
mkdirerr->popup_centered(Size2(250, 50));
@@ -850,11 +868,25 @@ void FileDialog::_make_dir() {
void FileDialog::_select_drive(int p_idx) {
String d = drives->get_item_text(p_idx);
- dir_access->change_dir(d);
+ _change_dir(d);
file->set_text("");
+ _push_history();
+}
+
+void FileDialog::_change_dir(const String &p_new_dir) {
+ if (root_prefix.is_empty()) {
+ dir_access->change_dir(p_new_dir);
+ } else {
+ String old_dir = dir_access->get_current_dir();
+ dir_access->change_dir(p_new_dir);
+ if (!dir_access->get_current_dir(false).begins_with(root_prefix)) {
+ dir_access->change_dir(old_dir);
+ return;
+ }
+ }
+
invalidate();
update_dir();
- _push_history();
}
void FileDialog::_update_drives(bool p_select) {
@@ -904,6 +936,8 @@ void FileDialog::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_edit"), &FileDialog::get_line_edit);
ClassDB::bind_method(D_METHOD("set_access", "access"), &FileDialog::set_access);
ClassDB::bind_method(D_METHOD("get_access"), &FileDialog::get_access);
+ ClassDB::bind_method(D_METHOD("set_root_subfolder", "dir"), &FileDialog::set_root_subfolder);
+ ClassDB::bind_method(D_METHOD("get_root_subfolder"), &FileDialog::get_root_subfolder);
ClassDB::bind_method(D_METHOD("set_show_hidden_files", "show"), &FileDialog::set_show_hidden_files);
ClassDB::bind_method(D_METHOD("is_showing_hidden_files"), &FileDialog::is_showing_hidden_files);
ClassDB::bind_method(D_METHOD("_update_file_name"), &FileDialog::update_file_name);
@@ -916,6 +950,7 @@ void FileDialog::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mode_overrides_title"), "set_mode_overrides_title", "is_mode_overriding_title");
ADD_PROPERTY(PropertyInfo(Variant::INT, "file_mode", PROPERTY_HINT_ENUM, "Open File,Open Files,Open Folder,Open Any,Save"), "set_file_mode", "get_file_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User Data,File System"), "set_access", "get_access");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "root_subfolder"), "set_root_subfolder", "get_root_subfolder");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "filters"), "set_filters", "get_filters");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir", PROPERTY_HINT_DIR, "", PROPERTY_USAGE_NONE), "set_current_dir", "get_current_dir");
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 2e326d2949..8b8d93920c 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -101,6 +101,8 @@ private:
void _push_history();
bool mode_overrides_title = true;
+ String root_subfolder;
+ String root_prefix;
static bool default_show_hidden_files;
bool show_hidden_files = false;
@@ -131,6 +133,7 @@ private:
void _go_back();
void _go_forward();
+ void _change_dir(const String &p_new_dir);
void _update_drives(bool p_select = true);
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
@@ -162,6 +165,9 @@ public:
void set_current_file(const String &p_file);
void set_current_path(const String &p_path);
+ void set_root_subfolder(const String &p_root);
+ String get_root_subfolder() const;
+
void set_mode_overrides_title(bool p_override);
bool is_mode_overriding_title() const;
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 0690acbe16..9459bed63b 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -382,7 +382,7 @@ void GradientEdit::_color_changed(const Color &p_color) {
emit_signal(SNAME("ramp_changed"));
}
-void GradientEdit::set_ramp(const Vector<real_t> &p_offsets, const Vector<Color> &p_colors) {
+void GradientEdit::set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors) {
ERR_FAIL_COND(p_offsets.size() != p_colors.size());
points.clear();
for (int i = 0; i < p_offsets.size(); i++) {
@@ -396,8 +396,8 @@ void GradientEdit::set_ramp(const Vector<real_t> &p_offsets, const Vector<Color>
update();
}
-Vector<real_t> GradientEdit::get_offsets() const {
- Vector<real_t> ret;
+Vector<float> GradientEdit::get_offsets() const {
+ Vector<float> ret;
for (int i = 0; i < points.size(); i++) {
ret.push_back(points[i].offset);
}
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 4e3c6525f9..3badcd45ba 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -67,8 +67,8 @@ protected:
static void _bind_methods();
public:
- void set_ramp(const Vector<real_t> &p_offsets, const Vector<Color> &p_colors);
- Vector<real_t> get_offsets() const;
+ void set_ramp(const Vector<float> &p_offsets, const Vector<Color> &p_colors);
+ Vector<float> get_offsets() const;
Vector<Color> get_colors() const;
void set_points(Vector<Gradient::Point> &p_points);
Vector<Gradient::Point> &get_points();
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 87706cd0d7..92016ca42e 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -44,26 +44,6 @@ struct _MinSizeCache {
bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- int value = p_value;
- if (value == -1) {
- if (opentype_features.has(tag)) {
- opentype_features.erase(tag);
- _shape();
- update();
- }
- } else {
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
- opentype_features[tag] = value;
- _shape();
- update();
- }
- }
- notify_property_list_changed();
- return true;
- }
if (!str.begins_with("slot/")) {
return false;
@@ -106,18 +86,6 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- if (opentype_features.has(tag)) {
- r_ret = opentype_features[tag];
- return true;
- } else {
- r_ret = -1;
- return true;
- }
- }
-
if (!str.begins_with("slot/")) {
return false;
}
@@ -156,12 +124,6 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
}
void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
- for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
- String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
- }
- p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
-
int idx = 0;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
@@ -471,7 +433,7 @@ void GraphNode::_shape() {
} else {
title_buf->set_direction((TextServer::Direction)text_direction);
}
- title_buf->add_string(title, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
+ title_buf->add_string(title, font, font_size, language);
}
#ifdef TOOLS_ENABLED
@@ -726,29 +688,6 @@ Control::TextDirection GraphNode::get_text_direction() const {
return text_direction;
}
-void GraphNode::clear_opentype_features() {
- opentype_features.clear();
- _shape();
- update();
-}
-
-void GraphNode::set_opentype_feature(const String &p_name, int p_value) {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
- opentype_features[tag] = p_value;
- _shape();
- update();
- }
-}
-
-int GraphNode::get_opentype_feature(const String &p_name) const {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag)) {
- return -1;
- }
- return opentype_features[tag];
-}
-
void GraphNode::set_language(const String &p_language) {
if (language != p_language) {
language = p_language;
@@ -1043,9 +982,6 @@ void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_title"), &GraphNode::get_title);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &GraphNode::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction"), &GraphNode::get_text_direction);
- ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &GraphNode::set_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &GraphNode::get_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_opentype_features"), &GraphNode::clear_opentype_features);
ClassDB::bind_method(D_METHOD("set_language", "language"), &GraphNode::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &GraphNode::get_language);
@@ -1105,8 +1041,6 @@ void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_overlay"), &GraphNode::get_overlay);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_position_offset", "get_position_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_close"), "set_show_close_button", "is_close_button_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resizable"), "set_resizable", "is_resizable");
@@ -1114,6 +1048,10 @@ void GraphNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "comment"), "set_comment", "is_comment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "overlay", PROPERTY_HINT_ENUM, "Disabled,Breakpoint,Position"), "set_overlay", "get_overlay");
+ ADD_GROUP("BiDi", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
+
ADD_SIGNAL(MethodInfo("position_offset_changed"));
ADD_SIGNAL(MethodInfo("slot_updated", PropertyInfo(Variant::INT, "idx")));
ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to")));
diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h
index f6c943dc89..0651eb5cc9 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -60,7 +60,6 @@ private:
String title;
Ref<TextLine> title_buf;
- Dictionary opentype_features;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
@@ -148,10 +147,6 @@ public:
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
- void set_opentype_feature(const String &p_name, int p_value);
- int get_opentype_feature(const String &p_name) const;
- void clear_opentype_features();
-
void set_language(const String &p_language);
String get_language() const;
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index aeb5338022..1d4ca4d196 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -43,7 +43,7 @@ void ItemList::_shape(int p_idx) {
} else {
item.text_buf->set_direction((TextServer::Direction)item.text_direction);
}
- item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.opentype_features, (!item.language.is_empty()) ? item.language : TranslationServer::get_singleton()->get_tool_locale());
+ item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.language);
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
item.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND);
} else {
@@ -117,35 +117,6 @@ Control::TextDirection ItemList::get_item_text_direction(int p_idx) const {
return items[p_idx].text_direction;
}
-void ItemList::clear_item_opentype_features(int p_idx) {
- ERR_FAIL_INDEX(p_idx, items.size());
- items.write[p_idx].opentype_features.clear();
- _shape(p_idx);
- update();
-}
-
-void ItemList::set_item_opentype_feature(int p_idx, const String &p_name, int p_value) {
- if (p_idx < 0) {
- p_idx += get_item_count();
- }
- ERR_FAIL_INDEX(p_idx, items.size());
- int32_t tag = TS->name_to_tag(p_name);
- if (!items[p_idx].opentype_features.has(tag) || (int)items[p_idx].opentype_features[tag] != p_value) {
- items.write[p_idx].opentype_features[tag] = p_value;
- _shape(p_idx);
- update();
- }
-}
-
-int ItemList::get_item_opentype_feature(int p_idx, const String &p_name) const {
- ERR_FAIL_INDEX_V(p_idx, items.size(), -1);
- int32_t tag = TS->name_to_tag(p_name);
- if (!items[p_idx].opentype_features.has(tag)) {
- return -1;
- }
- return items[p_idx].opentype_features[tag];
-}
-
void ItemList::set_item_language(int p_idx, const String &p_language) {
if (p_idx < 0) {
p_idx += get_item_count();
@@ -1656,10 +1627,6 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_text_direction", "idx", "direction"), &ItemList::set_item_text_direction);
ClassDB::bind_method(D_METHOD("get_item_text_direction", "idx"), &ItemList::get_item_text_direction);
- ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "idx", "tag", "value"), &ItemList::set_item_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "idx", "tag"), &ItemList::get_item_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "idx"), &ItemList::clear_item_opentype_features);
-
ClassDB::bind_method(D_METHOD("set_item_language", "idx", "language"), &ItemList::set_item_language);
ClassDB::bind_method(D_METHOD("get_item_language", "idx"), &ItemList::get_item_language);
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index a15b090149..c7d87da0b5 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -58,7 +58,6 @@ private:
Ref<Texture2D> tag_icon;
String text;
Ref<TextParagraph> text_buf;
- Dictionary opentype_features;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
@@ -145,10 +144,6 @@ public:
void set_item_text_direction(int p_idx, TextDirection p_text_direction);
TextDirection get_item_text_direction(int p_idx) const;
- void set_item_opentype_feature(int p_idx, const String &p_name, int p_value);
- int get_item_opentype_feature(int p_idx, const String &p_name) const;
- void clear_item_opentype_features(int p_idx);
-
void set_item_language(int p_idx, const String &p_language);
String get_item_language(int p_idx) const;
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 82ab7c2e18..5dec1df4a5 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -66,11 +66,11 @@ bool Label::is_uppercase() const {
int Label::get_line_height(int p_line) const {
Ref<Font> font = get_theme_font(SNAME("font"));
if (p_line >= 0 && p_line < lines_rid.size()) {
- return TS->shaped_text_get_size(lines_rid[p_line]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
+ return TS->shaped_text_get_size(lines_rid[p_line]).y;
} else if (lines_rid.size() > 0) {
int h = 0;
for (int i = 0; i < lines_rid.size(); i++) {
- h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
+ h = MAX(h, TS->shaped_text_get_size(lines_rid[i]).y);
}
return h;
} else {
@@ -83,7 +83,6 @@ void Label::_shape() {
int width = (get_size().width - style->get_minimum_size().width);
if (dirty || font_dirty) {
- String lang = (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale();
if (dirty) {
TS->shaped_text_clear(text_rid);
}
@@ -95,18 +94,21 @@ void Label::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
ERR_FAIL_COND(font.is_null());
- String text = (uppercase) ? TS->string_to_upper(xl_text, lang) : xl_text;
+ String text = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
text = text.substr(0, visible_chars);
}
if (dirty) {
- TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang);
+ TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, font->get_opentype_features(), language);
} else {
int spans = TS->shaped_get_span_count(text_rid);
for (int i = 0; i < spans; i++) {
- TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features);
+ TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, font->get_opentype_features());
}
}
+ for (int i = 0; i < TextServer::SPACING_MAX; i++) {
+ TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
+ }
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text));
dirty = false;
font_dirty = false;
@@ -233,7 +235,7 @@ void Label::_update_visible() {
minsize.height = 0;
int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
for (int64_t i = lines_skipped; i < last_line; i++) {
- minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
+ minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) {
break;
}
@@ -314,7 +316,7 @@ void Label::_notification(int p_what) {
// Get number of lines to fit to the height.
for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
break;
}
@@ -334,7 +336,7 @@ void Label::_notification(int p_what) {
int total_glyphs = 0;
total_h = 0;
for (int64_t i = lines_skipped; i < last_line; i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
}
int visible_glyphs = total_glyphs * percent_visible;
@@ -374,7 +376,7 @@ void Label::_notification(int p_what) {
for (int i = lines_skipped; i < last_line; i++) {
Size2 line_size = TS->shaped_text_get_size(lines_rid[i]);
ofs.x = 0;
- ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP);
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]);
switch (horizontal_alignment) {
case HORIZONTAL_ALIGNMENT_FILL:
if (rtl && autowrap_mode != TextServer::AUTOWRAP_OFF) {
@@ -527,7 +529,7 @@ void Label::_notification(int p_what) {
}
}
}
- ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM);
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing;
}
} break;
@@ -551,7 +553,7 @@ Size2 Label::get_minimum_size() const {
Size2 min_size = minsize;
Ref<Font> font = get_theme_font(SNAME("font"));
- min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size"))) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM));
+ min_size.height = MAX(min_size.height, font->get_height(get_theme_font_size(SNAME("font_size"))));
Size2 min_style = get_theme_stylebox(SNAME("normal"))->get_minimum_size();
if (autowrap_mode != TextServer::AUTOWRAP_OFF) {
@@ -582,7 +584,7 @@ int Label::get_visible_line_count() const {
int lines_visible = 0;
float total_h = 0.0;
for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
break;
}
@@ -674,29 +676,6 @@ Control::TextDirection Label::get_text_direction() const {
return text_direction;
}
-void Label::clear_opentype_features() {
- opentype_features.clear();
- font_dirty = true;
- update();
-}
-
-void Label::set_opentype_feature(const String &p_name, int p_value) {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
- opentype_features[tag] = p_value;
- font_dirty = true;
- update();
- }
-}
-
-int Label::get_opentype_feature(const String &p_name) const {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag)) {
- return -1;
- }
- return opentype_features[tag];
-}
-
void Label::set_language(const String &p_language) {
if (language != p_language) {
language = p_language;
@@ -818,56 +797,6 @@ int Label::get_total_character_count() const {
return xl_text.length();
}
-bool Label::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- int value = p_value;
- if (value == -1) {
- if (opentype_features.has(tag)) {
- opentype_features.erase(tag);
- font_dirty = true;
- update();
- }
- } else {
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
- opentype_features[tag] = value;
- font_dirty = true;
- update();
- }
- }
- notify_property_list_changed();
- return true;
- }
-
- return false;
-}
-
-bool Label::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- if (opentype_features.has(tag)) {
- r_ret = opentype_features[tag];
- return true;
- } else {
- r_ret = -1;
- return true;
- }
- }
- return false;
-}
-
-void Label::_get_property_list(List<PropertyInfo> *p_list) const {
- for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
- String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
- }
- p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
-}
-
void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label::set_horizontal_alignment);
ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &Label::get_horizontal_alignment);
@@ -877,9 +806,6 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &Label::get_text);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction"), &Label::get_text_direction);
- ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &Label::set_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &Label::get_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_opentype_features"), &Label::clear_opentype_features);
ClassDB::bind_method(D_METHOD("set_language", "language"), &Label::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &Label::get_language);
ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label::set_autowrap_mode);
@@ -924,11 +850,9 @@ void Label::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
- ADD_GROUP("Locale", "");
+ ADD_GROUP("BiDi", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
-
- ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
}
diff --git a/scene/gui/label.h b/scene/gui/label.h
index fac3d75a1b..a59d35950d 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -53,7 +53,6 @@ private:
RID text_rid;
Vector<RID> lines_rid;
- Dictionary opentype_features;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
@@ -74,10 +73,6 @@ protected:
static void _bind_methods();
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
public:
virtual Size2 get_minimum_size() const override;
@@ -93,10 +88,6 @@ public:
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
- void set_opentype_feature(const String &p_name, int p_value);
- int get_opentype_feature(const String &p_name) const;
- void clear_opentype_features();
-
void set_language(const String &p_language);
String get_language() const;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 540250c8e9..39f8f23cd8 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -744,6 +744,17 @@ void LineEdit::_notification(int p_what) {
update();
} break;
+ case NOTIFICATION_INTERNAL_PROCESS: {
+ if (caret_blinking) {
+ caret_blink_timer += get_process_delta_time();
+
+ if (caret_blink_timer >= caret_blink_speed) {
+ caret_blink_timer = 0.0;
+ _toggle_draw_caret();
+ }
+ }
+ } break;
+
case NOTIFICATION_DRAW: {
if ((!has_focus() && !(menu && menu->has_focus()) && !caret_force_displayed) || !window_has_focus) {
draw_caret = false;
@@ -776,7 +787,7 @@ void LineEdit::_notification(int p_what) {
int x_ofs = 0;
bool using_placeholder = text.is_empty() && ime_text.is_empty();
float text_width = TS->shaped_text_get_size(text_rid).x;
- float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
+ float text_height = TS->shaped_text_get_size(text_rid).y;
switch (alignment) {
case HORIZONTAL_ALIGNMENT_FILL:
@@ -991,8 +1002,9 @@ void LineEdit::_notification(int p_what) {
case NOTIFICATION_FOCUS_ENTER: {
if (!caret_force_displayed) {
if (caret_blink_enabled) {
- if (caret_blink_timer->is_stopped()) {
- caret_blink_timer->start();
+ if (!caret_blinking) {
+ caret_blinking = true;
+ caret_blink_timer = 0.0;
}
} else {
draw_caret = true;
@@ -1010,7 +1022,7 @@ void LineEdit::_notification(int p_what) {
case NOTIFICATION_FOCUS_EXIT: {
if (caret_blink_enabled && !caret_force_displayed) {
- caret_blink_timer->stop();
+ caret_blinking = false;
}
if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) {
@@ -1318,14 +1330,16 @@ bool LineEdit::is_caret_blink_enabled() const {
void LineEdit::set_caret_blink_enabled(const bool p_enabled) {
caret_blink_enabled = p_enabled;
+ set_process_internal(p_enabled);
if (has_focus() || caret_force_displayed) {
if (p_enabled) {
- if (caret_blink_timer->is_stopped()) {
- caret_blink_timer->start();
+ if (!caret_blinking) {
+ caret_blinking = true;
+ caret_blink_timer = 0.0;
}
} else {
- caret_blink_timer->stop();
+ caret_blinking = false;
}
}
@@ -1345,20 +1359,19 @@ void LineEdit::set_caret_force_displayed(const bool p_enabled) {
}
float LineEdit::get_caret_blink_speed() const {
- return caret_blink_timer->get_wait_time();
+ return caret_blink_speed;
}
void LineEdit::set_caret_blink_speed(const float p_speed) {
ERR_FAIL_COND(p_speed <= 0);
- caret_blink_timer->set_wait_time(p_speed);
+ caret_blink_speed = p_speed;
}
void LineEdit::_reset_caret_blink_timer() {
if (caret_blink_enabled) {
draw_caret = true;
if (has_focus()) {
- caret_blink_timer->stop();
- caret_blink_timer->start();
+ caret_blink_timer = 0.0;
update();
}
}
@@ -1438,29 +1451,6 @@ Control::TextDirection LineEdit::get_text_direction() const {
return text_direction;
}
-void LineEdit::clear_opentype_features() {
- opentype_features.clear();
- _shape();
- update();
-}
-
-void LineEdit::set_opentype_feature(const String &p_name, int p_value) {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
- opentype_features[tag] = p_value;
- _shape();
- update();
- }
-}
-
-int LineEdit::get_opentype_feature(const String &p_name) const {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag)) {
- return -1;
- }
- return opentype_features[tag];
-}
-
void LineEdit::set_language(const String &p_language) {
if (language != p_language) {
language = p_language;
@@ -1675,7 +1665,7 @@ Size2 LineEdit::get_minimum_size() const {
Size2 min_size;
// Minimum size of text.
- float em_space_size = font->get_char_size('M', 0, font_size).x;
+ float em_space_size = font->get_char_size('M', font_size).x;
min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size;
if (expand_to_text_length) {
@@ -1683,7 +1673,7 @@ Size2 LineEdit::get_minimum_size() const {
min_size.width = MAX(min_size.width, full_width + em_space_size);
}
- min_size.height = MAX(TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM), font->get_height(font_size));
+ min_size.height = MAX(TS->shaped_text_get_size(text_rid).y, font->get_height(font_size));
// Take icons into account.
int icon_max_width = 0;
@@ -2142,7 +2132,10 @@ void LineEdit::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
ERR_FAIL_COND(font.is_null());
- TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
+ TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language);
+ for (int i = 0; i < TextServer::SPACING_MAX; i++) {
+ TS->shaped_text_set_spacing(text_rid, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
+ }
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t));
full_width = TS->shaped_text_get_size(text_rid).x;
@@ -2223,56 +2216,6 @@ Key LineEdit::_get_menu_action_accelerator(const String &p_action) {
}
}
-bool LineEdit::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- int value = p_value;
- if (value == -1) {
- if (opentype_features.has(tag)) {
- opentype_features.erase(tag);
- _shape();
- update();
- }
- } else {
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
- opentype_features[tag] = value;
- _shape();
- update();
- }
- }
- notify_property_list_changed();
- return true;
- }
-
- return false;
-}
-
-bool LineEdit::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- if (opentype_features.has(tag)) {
- r_ret = opentype_features[tag];
- return true;
- } else {
- r_ret = -1;
- return true;
- }
- }
- return false;
-}
-
-void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const {
- for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
- String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
- }
- p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
-}
-
void LineEdit::_validate_property(PropertyInfo &property) const {
if (!caret_blink_enabled && property.name == "caret_blink_speed") {
property.usage = PROPERTY_USAGE_NO_EDITOR;
@@ -2298,9 +2241,6 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_draw_control_chars", "enable"), &LineEdit::set_draw_control_chars);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &LineEdit::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction"), &LineEdit::get_text_direction);
- ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &LineEdit::set_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &LineEdit::get_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_opentype_features"), &LineEdit::clear_opentype_features);
ClassDB::bind_method(D_METHOD("set_language", "language"), &LineEdit::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &LineEdit::get_language);
ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "parser"), &LineEdit::set_structured_text_bidi_override);
@@ -2406,18 +2346,20 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
- ADD_GROUP("Structured Text", "structured_text_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
+
ADD_GROUP("Caret", "caret_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "set_caret_blink_speed", "get_caret_blink_speed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_column", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_caret_column", "get_caret_column");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled");
+
+ ADD_GROUP("BiDi", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
}
void LineEdit::_ensure_menu() {
@@ -2508,10 +2450,6 @@ LineEdit::LineEdit(const String &p_placeholder) {
set_mouse_filter(MOUSE_FILTER_STOP);
set_process_unhandled_key_input(true);
- caret_blink_timer = memnew(Timer);
- add_child(caret_blink_timer, false, INTERNAL_MODE_FRONT);
- caret_blink_timer->set_wait_time(0.65);
- caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret));
set_caret_blink_enabled(false);
set_placeholder(p_placeholder);
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 0fb178fca4..557da35bfd 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -105,7 +105,6 @@ private:
int scroll_offset = 0;
int max_length = 0; // 0 for no maximum.
- Dictionary opentype_features;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
TextDirection input_direction = TEXT_DIRECTION_LTR;
@@ -160,7 +159,9 @@ private:
bool caret_blink_enabled = false;
bool caret_force_displayed = false;
bool draw_caret = true;
- Timer *caret_blink_timer = nullptr;
+ float caret_blink_speed = 0.65;
+ double caret_blink_timer = 0.0;
+ bool caret_blinking = false;
bool _is_over_clear_button(const Point2 &p_pos) const;
@@ -208,9 +209,6 @@ protected:
virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
virtual void gui_input(const Ref<InputEvent> &p_event) override;
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
void _validate_property(PropertyInfo &property) const override;
public:
@@ -246,10 +244,6 @@ public:
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
- void set_opentype_feature(const String &p_name, int p_value);
- int get_opentype_feature(const String &p_name) const;
- void clear_opentype_features();
-
void set_language(const String &p_language);
String get_language() const;
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index dca6437519..30c0bb3321 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -43,7 +43,7 @@ void LinkButton::_shape() {
text_buf->set_direction((TextServer::Direction)text_direction);
}
TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, xl_text));
- text_buf->add_string(xl_text, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text_buf->add_string(xl_text, font, font_size, language);
}
void LinkButton::set_text(const String &p_text) {
@@ -96,29 +96,6 @@ Control::TextDirection LinkButton::get_text_direction() const {
return text_direction;
}
-void LinkButton::clear_opentype_features() {
- opentype_features.clear();
- _shape();
- update();
-}
-
-void LinkButton::set_opentype_feature(const String &p_name, int p_value) {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
- opentype_features[tag] = p_value;
- _shape();
- update();
- }
-}
-
-int LinkButton::get_opentype_feature(const String &p_name) const {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag)) {
- return -1;
- }
- return opentype_features[tag];
-}
-
void LinkButton::set_language(const String &p_language) {
if (language != p_language) {
language = p_language;
@@ -237,64 +214,11 @@ void LinkButton::_notification(int p_what) {
}
}
-bool LinkButton::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- int value = p_value;
- if (value == -1) {
- if (opentype_features.has(tag)) {
- opentype_features.erase(tag);
- _shape();
- update();
- }
- } else {
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
- opentype_features[tag] = value;
- _shape();
- update();
- }
- }
- notify_property_list_changed();
- return true;
- }
-
- return false;
-}
-
-bool LinkButton::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- if (opentype_features.has(tag)) {
- r_ret = opentype_features[tag];
- return true;
- } else {
- r_ret = -1;
- return true;
- }
- }
- return false;
-}
-
-void LinkButton::_get_property_list(List<PropertyInfo> *p_list) const {
- for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
- String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
- }
- p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
-}
-
void LinkButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_text", "text"), &LinkButton::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &LinkButton::get_text);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &LinkButton::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction"), &LinkButton::get_text_direction);
- ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &LinkButton::set_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &LinkButton::get_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_opentype_features"), &LinkButton::clear_opentype_features);
ClassDB::bind_method(D_METHOD("set_language", "language"), &LinkButton::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &LinkButton::get_language);
ClassDB::bind_method(D_METHOD("set_underline_mode", "underline_mode"), &LinkButton::set_underline_mode);
@@ -309,10 +233,11 @@ void LinkButton::_bind_methods() {
BIND_ENUM_CONSTANT(UNDERLINE_MODE_NEVER);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode");
+
+ ADD_GROUP("BiDi", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode");
- ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
}
diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h
index 6d2dcbde84..54a31f06ce 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -50,7 +50,6 @@ private:
Ref<TextLine> text_buf;
UnderlineMode underline_mode = UNDERLINE_MODE_ALWAYS;
- Dictionary opentype_features;
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
@@ -63,10 +62,6 @@ protected:
void _notification(int p_what);
static void _bind_methods();
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
public:
void set_text(const String &p_text);
String get_text() const;
@@ -80,10 +75,6 @@ public:
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
- void set_opentype_feature(const String &p_name, int p_value);
- int get_opentype_feature(const String &p_name) const;
- void clear_opentype_features();
-
void set_language(const String &p_language);
String get_language() const;
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 5931c112eb..3a31246b17 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -760,11 +760,11 @@ void PopupMenu::_shape_item(int p_item) {
} else {
items.write[p_item].text_buf->set_direction((TextServer::Direction)items[p_item].text_direction);
}
- items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].opentype_features, !items[p_item].language.is_empty() ? items[p_item].language : TranslationServer::get_singleton()->get_tool_locale());
+ items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].language);
items.write[p_item].accel_text_buf->clear();
items.write[p_item].accel_text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
- items.write[p_item].accel_text_buf->add_string(_get_accel_text(items.write[p_item]), font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ items.write[p_item].accel_text_buf->add_string(_get_accel_text(items.write[p_item]), font, font_size);
items.write[p_item].dirty = false;
}
}
@@ -1067,29 +1067,6 @@ void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_tex
}
}
-void PopupMenu::clear_item_opentype_features(int p_item) {
- if (p_item < 0) {
- p_item += get_item_count();
- }
- ERR_FAIL_INDEX(p_item, items.size());
- items.write[p_item].opentype_features.clear();
- items.write[p_item].dirty = true;
- control->update();
-}
-
-void PopupMenu::set_item_opentype_feature(int p_item, const String &p_name, int p_value) {
- if (p_item < 0) {
- p_item += get_item_count();
- }
- ERR_FAIL_INDEX(p_item, items.size());
- int32_t tag = TS->name_to_tag(p_name);
- if (!items[p_item].opentype_features.has(tag) || (int)items[p_item].opentype_features[tag] != p_value) {
- items.write[p_item].opentype_features[tag] = p_value;
- items.write[p_item].dirty = true;
- control->update();
- }
-}
-
void PopupMenu::set_item_language(int p_item, const String &p_language) {
if (p_item < 0) {
p_item += get_item_count();
@@ -1195,15 +1172,6 @@ Control::TextDirection PopupMenu::get_item_text_direction(int p_item) const {
return items[p_item].text_direction;
}
-int PopupMenu::get_item_opentype_feature(int p_item, const String &p_name) const {
- ERR_FAIL_INDEX_V(p_item, items.size(), -1);
- int32_t tag = TS->name_to_tag(p_name);
- if (!items[p_item].opentype_features.has(tag)) {
- return -1;
- }
- return items[p_item].opentype_features[tag];
-}
-
String PopupMenu::get_item_language(int p_item) const {
ERR_FAIL_INDEX_V(p_item, items.size(), "");
return items[p_item].language;
@@ -1853,7 +1821,6 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_text", "index", "text"), &PopupMenu::set_item_text);
ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction);
- ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "index", "tag", "value"), &PopupMenu::set_item_opentype_feature);
ClassDB::bind_method(D_METHOD("set_item_language", "index", "language"), &PopupMenu::set_item_language);
ClassDB::bind_method(D_METHOD("set_item_icon", "index", "icon"), &PopupMenu::set_item_icon);
ClassDB::bind_method(D_METHOD("set_item_checked", "index", "checked"), &PopupMenu::set_item_checked);
@@ -1876,8 +1843,6 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_text", "index"), &PopupMenu::get_item_text);
ClassDB::bind_method(D_METHOD("get_item_text_direction", "index"), &PopupMenu::get_item_text_direction);
- ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "index", "tag"), &PopupMenu::get_item_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "index"), &PopupMenu::clear_item_opentype_features);
ClassDB::bind_method(D_METHOD("get_item_language", "index"), &PopupMenu::get_item_language);
ClassDB::bind_method(D_METHOD("get_item_icon", "index"), &PopupMenu::get_item_icon);
ClassDB::bind_method(D_METHOD("is_item_checked", "index"), &PopupMenu::is_item_checked);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 8218c6122e..daa38b0e6d 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -47,7 +47,6 @@ class PopupMenu : public Popup {
Ref<TextLine> text_buf;
Ref<TextLine> accel_text_buf;
- Dictionary opentype_features;
String language;
Control::TextDirection text_direction = Control::TEXT_DIRECTION_AUTO;
@@ -171,8 +170,6 @@ public:
void set_item_text(int p_idx, const String &p_text);
void set_item_text_direction(int p_idx, Control::TextDirection p_text_direction);
- void set_item_opentype_feature(int p_idx, const String &p_name, int p_value);
- void clear_item_opentype_features(int p_idx);
void set_item_language(int p_idx, const String &p_language);
void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon);
void set_item_checked(int p_idx, bool p_checked);
@@ -195,7 +192,6 @@ public:
String get_item_text(int p_idx) const;
Control::TextDirection get_item_text_direction(int p_idx) const;
- int get_item_opentype_feature(int p_idx, const String &p_name) const;
String get_item_language(int p_idx) const;
int get_item_idx_from_text(const String &text) const;
Ref<Texture2D> get_item_icon(int p_idx) const;
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 0516c8e722..05824d54f1 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -227,8 +227,10 @@ void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<
if (font_size == -1) {
font_size = p_base_font_size;
}
- Dictionary font_ftr = _find_font_features(it);
- TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font_ftr);
+ TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font->get_opentype_features());
+ for (int j = 0; j < TextServer::SPACING_MAX; j++) {
+ TS->shaped_text_set_spacing(t, TextServer::SpacingType(j), font->get_spacing(TextServer::SpacingType(j)));
+ }
}
}
@@ -263,7 +265,7 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font
if (tab_size > 0) { // Align inline tabs.
Vector<float> tabs;
- tabs.push_back(tab_size * p_base_font->get_char_size(' ', 0, p_base_font_size).width);
+ tabs.push_back(tab_size * p_base_font->get_char_size(' ', p_base_font_size).width);
l.text_buf->tab_align(tabs);
}
@@ -453,7 +455,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
if (tab_size > 0) { // Align inline tabs.
Vector<float> tabs;
- tabs.push_back(tab_size * p_base_font->get_char_size(' ', 0, p_base_font_size).width);
+ tabs.push_back(tab_size * p_base_font->get_char_size(' ', p_base_font_size).width);
l.text_buf->tab_align(tabs);
}
@@ -483,7 +485,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
if (font_size == -1) {
font_size = p_base_font_size;
}
- l.text_buf->add_string("\n", font, font_size, Dictionary(), "");
+ l.text_buf->add_string("\n", font, font_size);
text += "\n";
l.char_count++;
remaining_characters--;
@@ -498,7 +500,6 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
if (font_size == -1) {
font_size = p_base_font_size;
}
- Dictionary font_ftr = _find_font_features(it);
String lang = _find_language(it);
String tx = t->text;
if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters >= 0) {
@@ -506,7 +507,7 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
remaining_characters -= tx.length();
- l.text_buf->add_string(tx, font, font_size, font_ftr, lang, (uint64_t)it);
+ l.text_buf->add_string(tx, font, font_size, lang, (uint64_t)it);
text += tx;
l.char_count += tx.length();
} break;
@@ -837,7 +838,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
//draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS
- off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top();
+ off.y += TS->shaped_text_get_ascent(rid);
// Draw inlined objects.
Array objects = TS->shaped_text_get_objects(rid);
for (int i = 0; i < objects.size(); i++) {
@@ -1299,7 +1300,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
// Draw foreground color box
_draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 1);
- off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom();
+ off.y += TS->shaped_text_get_descent(rid);
}
return line_count;
@@ -1394,7 +1395,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
} break;
}
- off.y += TS->shaped_text_get_ascent(rid) + l.text_buf->get_spacing_top();
+ off.y += TS->shaped_text_get_ascent(rid);
Array objects = TS->shaped_text_get_objects(rid);
for (int i = 0; i < objects.size(); i++) {
@@ -1491,7 +1492,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
return table_offy;
}
- off.y += TS->shaped_text_get_descent(rid) + l.text_buf->get_spacing_bottom() + get_theme_constant(SNAME("line_separation"));
+ off.y += TS->shaped_text_get_descent(rid) + get_theme_constant(SNAME("line_separation"));
}
// Text line hit.
@@ -2113,21 +2114,6 @@ int RichTextLabel::_find_outline_size(Item *p_item, int p_default) {
return p_default;
}
-Dictionary RichTextLabel::_find_font_features(Item *p_item) {
- Item *ffitem = p_item;
-
- while (ffitem) {
- if (ffitem->type == ITEM_FONT_FEATURES) {
- ItemFontFeatures *fi = static_cast<ItemFontFeatures *>(ffitem);
- return fi->opentype_features;
- }
-
- ffitem = ffitem->parent;
- }
-
- return Dictionary();
-}
-
RichTextLabel::ItemDropcap *RichTextLabel::_find_dc_item(Item *p_item) {
Item *item = p_item;
@@ -2204,7 +2190,7 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int
if (font_size == -1) {
font_size = p_base_font_size;
}
- margin += tab_size * font->get_char_size(' ', 0, font_size).width;
+ margin += tab_size * font->get_char_size(' ', font_size).width;
} else if (item->type == ITEM_LIST) {
Ref<Font> font = _find_font(item);
@@ -2215,7 +2201,7 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int
if (font_size == -1) {
font_size = p_base_font_size;
}
- margin += tab_size * font->get_char_size(' ', 0, font_size).width;
+ margin += tab_size * font->get_char_size(' ', font_size).width;
}
item = item->parent;
@@ -2584,8 +2570,8 @@ void RichTextLabel::_process_line_caches() {
MutexLock data_lock(data_mutex);
Rect2 text_rect = _get_text_rect();
- Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
+ Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
int ctrl_height = get_size().height;
int fi = main->first_invalid_line.load();
int total_chars = (fi == 0) ? 0 : (main->lines[fi].char_offset + main->lines[fi].char_count);
@@ -2950,25 +2936,14 @@ void RichTextLabel::push_font_size(int p_font_size) {
_add_item(item, true);
}
-void RichTextLabel::push_font_features(const Dictionary &p_features) {
- _stop_thread();
- MutexLock data_lock(data_mutex);
-
- ERR_FAIL_COND(current->type == ITEM_TABLE);
- ItemFontFeatures *item = memnew(ItemFontFeatures);
-
- item->opentype_features = p_features;
- _add_item(item, true);
-}
-
-void RichTextLabel::push_outline_size(int p_font_size) {
+void RichTextLabel::push_outline_size(int p_ol_size) {
_stop_thread();
MutexLock data_lock(data_mutex);
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemOutlineSize *item = memnew(ItemOutlineSize);
- item->outline_size = p_font_size;
+ item->outline_size = p_ol_size;
_add_item(item, true);
}
@@ -3840,8 +3815,8 @@ void RichTextLabel::append_text(const String &p_bbcode) {
tag_stack.push_front("hint");
} else if (tag.begins_with("dropcap")) {
Vector<String> subtag = tag.substr(5, tag.length()).split(" ");
- Ref<Font> f = get_theme_font(SNAME("normal_font"));
int fs = get_theme_font_size(SNAME("normal_font_size")) * 3;
+ Ref<Font> f = get_theme_font(SNAME("normal_font"));
Color color = get_theme_color(SNAME("default_color"));
Color outline_color = get_theme_color(SNAME("outline_color"));
int outline_size = get_theme_constant(SNAME("outline_size"));
@@ -3974,64 +3949,127 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front("outline_color");
- } else if (tag.begins_with("font=")) {
- String fnt = tag.substr(5, tag.length());
-
- Ref<Font> font = ResourceLoader::load(fnt, "Font");
- if (font.is_valid()) {
- push_font(font);
- } else {
- push_font(normal_font);
- }
-
- pos = brk_end + 1;
- tag_stack.push_front("font");
} else if (tag.begins_with("font_size=")) {
int fnt_size = tag.substr(10, tag.length()).to_int();
push_font_size(fnt_size);
pos = brk_end + 1;
tag_stack.push_front("font_size");
+
} else if (tag.begins_with("opentype_features=")) {
String fnt_ftr = tag.substr(18, tag.length());
Vector<String> subtag = fnt_ftr.split(",");
- Dictionary ftrs;
- for (int i = 0; i < subtag.size(); i++) {
- Vector<String> subtag_a = subtag[i].split("=");
- if (subtag_a.size() == 2) {
- ftrs[TS->name_to_tag(subtag_a[0])] = subtag_a[1].to_int();
- } else if (subtag_a.size() == 1) {
- ftrs[TS->name_to_tag(subtag_a[0])] = 1;
+ if (subtag.size() > 0) {
+ Ref<Font> font = _find_font(current);
+ if (font.is_null()) {
+ font = normal_font;
}
+ Ref<FontVariation> fc;
+ fc.instantiate();
+ fc->set_base_font(font);
+ Dictionary features;
+ for (int i = 0; i < subtag.size(); i++) {
+ Vector<String> subtag_a = subtag[i].split("=");
+ if (subtag_a.size() == 2) {
+ features[TS->name_to_tag(subtag_a[0])] = subtag_a[1].to_int();
+ } else if (subtag_a.size() == 1) {
+ features[TS->name_to_tag(subtag_a[0])] = 1;
+ }
+ }
+ fc->set_opentype_features(features);
+ push_font(fc);
}
- push_font_features(ftrs);
pos = brk_end + 1;
tag_stack.push_front("opentype_features");
+
+ } else if (tag.begins_with("font=")) {
+ String fnt = tag.substr(5, tag.length());
+
+ Ref<Font> fc = ResourceLoader::load(fnt, "Font");
+ if (fc.is_valid()) {
+ push_font(fc);
+ }
+
+ pos = brk_end + 1;
+ tag_stack.push_front("font");
+
} else if (tag.begins_with("font ")) {
Vector<String> subtag = tag.substr(2, tag.length()).split(" ");
+ Ref<FontVariation> fc;
+ fc.instantiate();
for (int i = 1; i < subtag.size(); i++) {
Vector<String> subtag_a = subtag[i].split("=", true, 2);
if (subtag_a.size() == 2) {
if (subtag_a[0] == "name" || subtag_a[0] == "n") {
String fnt = subtag_a[1];
- Ref<Font> font = ResourceLoader::load(fnt, "Font");
- if (font.is_valid()) {
- push_font(font);
- } else {
- push_font(normal_font);
+ Ref<Font> font_data = ResourceLoader::load(fnt, "Font");
+ if (font_data.is_valid()) {
+ fc->set_base_font(font_data);
}
} else if (subtag_a[0] == "size" || subtag_a[0] == "s") {
int fnt_size = subtag_a[1].to_int();
- push_font_size(fnt_size);
+ if (fnt_size > 0) {
+ push_font_size(fnt_size);
+ }
+ } else if (subtag_a[0] == "glyph_spacing" || subtag_a[0] == "gl") {
+ int spacing = subtag_a[1].to_int();
+ fc->set_spacing(TextServer::SPACING_GLYPH, spacing);
+ } else if (subtag_a[0] == "space_spacing" || subtag_a[0] == "sp") {
+ int spacing = subtag_a[1].to_int();
+ fc->set_spacing(TextServer::SPACING_SPACE, spacing);
+ } else if (subtag_a[0] == "top_spacing" || subtag_a[0] == "top") {
+ int spacing = subtag_a[1].to_int();
+ fc->set_spacing(TextServer::SPACING_TOP, spacing);
+ } else if (subtag_a[0] == "bottom_spacing" || subtag_a[0] == "bt") {
+ int spacing = subtag_a[1].to_int();
+ fc->set_spacing(TextServer::SPACING_BOTTOM, spacing);
+ } else if (subtag_a[0] == "embolden" || subtag_a[0] == "emb") {
+ float emb = subtag_a[1].to_float();
+ fc->set_variation_embolden(emb);
+ } else if (subtag_a[0] == "face_index" || subtag_a[0] == "fi") {
+ int fi = subtag_a[1].to_int();
+ fc->set_variation_face_index(fi);
+ } else if (subtag_a[0] == "slant" || subtag_a[0] == "sln") {
+ float slant = subtag_a[1].to_float();
+ fc->set_variation_transform(Transform2D(1.0, slant, 0.0, 1.0, 0.0, 0.0));
+ } else if (subtag_a[0] == "opentype_variation" || subtag_a[0] == "otv") {
+ Dictionary variations;
+ if (!subtag_a[1].is_empty()) {
+ Vector<String> variation_tags = subtag_a[1].split(",");
+ for (int j = 0; j < variation_tags.size(); j++) {
+ Vector<String> subtag_b = variation_tags[j].split("=");
+ if (subtag_b.size() == 2) {
+ variations[TS->name_to_tag(subtag_b[0])] = subtag_b[1].to_float();
+ }
+ }
+ fc->set_variation_opentype(variations);
+ }
+ } else if (subtag_a[0] == "opentype_features" || subtag_a[0] == "otf") {
+ Dictionary features;
+ if (!subtag_a[1].is_empty()) {
+ Vector<String> feature_tags = subtag_a[1].split(",");
+ for (int j = 0; j < feature_tags.size(); j++) {
+ Vector<String> subtag_b = feature_tags[j].split("=");
+ if (subtag_b.size() == 2) {
+ features[TS->name_to_tag(subtag_b[0])] = subtag_b[1].to_float();
+ } else if (subtag_b.size() == 1) {
+ features[TS->name_to_tag(subtag_b[0])] = 1;
+ }
+ }
+ fc->set_opentype_features(features);
+ }
}
}
}
-
+ push_font(fc);
pos = brk_end + 1;
tag_stack.push_front("font");
+
} else if (tag.begins_with("outline_size=")) {
int fnt_size = tag.substr(13, tag.length()).to_int();
- push_outline_size(fnt_size);
+ if (fnt_size > 0) {
+ push_outline_size(fnt_size);
+ }
pos = brk_end + 1;
tag_stack.push_front("outline_size");
@@ -4854,7 +4892,6 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size);
- ClassDB::bind_method(D_METHOD("push_font_features", "opentype_features"), &RichTextLabel::push_font_features);
ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal);
ClassDB::bind_method(D_METHOD("push_bold"), &RichTextLabel::push_bold);
ClassDB::bind_method(D_METHOD("push_bold_italics"), &RichTextLabel::push_bold_italics);
@@ -5018,11 +5055,9 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
- ADD_GROUP("Locale", "");
+ ADD_GROUP("BiDi", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
-
- ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index c697320976..3b6175e9cf 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -186,11 +186,6 @@ private:
ItemFontSize() { type = ITEM_FONT_SIZE; }
};
- struct ItemFontFeatures : public Item {
- Dictionary opentype_features;
- ItemFontFeatures() { type = ITEM_FONT_FEATURES; }
- };
-
struct ItemColor : public Item {
Color color;
ItemColor() { type = ITEM_COLOR; }
@@ -466,9 +461,8 @@ private:
Item *_get_item_at_pos(Item *p_item_from, Item *p_item_to, int p_position);
void _find_frame(Item *p_item, ItemFrame **r_frame, int *r_line);
- Ref<Font> _find_font(Item *p_item);
int _find_font_size(Item *p_item);
- Dictionary _find_font_features(Item *p_item);
+ Ref<Font> _find_font(Item *p_item);
int _find_outline_size(Item *p_item, int p_default);
ItemList *_find_list_item(Item *p_item);
ItemDropcap *_find_dc_item(Item *p_item);
@@ -525,7 +519,6 @@ public:
void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0));
void push_font(const Ref<Font> &p_font);
void push_font_size(int p_font_size);
- void push_font_features(const Dictionary &p_features);
void push_outline_size(int p_font_size);
void push_normal();
void push_bold();
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index e50d7e765c..890e349afb 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -62,7 +62,7 @@ void SpinBox::_text_submitted(const String &p_string) {
return;
}
- Variant value = expr->execute(Array(), nullptr, false);
+ Variant value = expr->execute(Array(), nullptr, false, true);
if (value.get_type() != Variant::NIL) {
set_value(value);
}
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index b4c90596f9..d36a364677 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -311,7 +311,7 @@ void TabBar::_shape(int p_tab) {
tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction);
}
- tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, !tabs[p_tab].language.is_empty() ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
+ tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].language);
}
void TabBar::_notification(int p_what) {
@@ -667,48 +667,6 @@ Control::TextDirection TabBar::get_tab_text_direction(int p_tab) const {
return tabs[p_tab].text_direction;
}
-void TabBar::clear_tab_opentype_features(int p_tab) {
- ERR_FAIL_INDEX(p_tab, tabs.size());
- tabs.write[p_tab].opentype_features.clear();
-
- _shape(p_tab);
- _update_cache();
- _ensure_no_over_offset();
- if (scroll_to_selected) {
- ensure_tab_visible(current);
- }
- update();
- update_minimum_size();
-}
-
-void TabBar::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) {
- ERR_FAIL_INDEX(p_tab, tabs.size());
-
- int32_t tag = TS->name_to_tag(p_name);
- if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) {
- tabs.write[p_tab].opentype_features[tag] = p_value;
-
- _shape(p_tab);
- _update_cache();
- _ensure_no_over_offset();
- if (scroll_to_selected) {
- ensure_tab_visible(current);
- }
- update();
- update_minimum_size();
- }
-}
-
-int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const {
- ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1);
-
- int32_t tag = TS->name_to_tag(p_name);
- if (!tabs[p_tab].opentype_features.has(tag)) {
- return -1;
- }
- return tabs[p_tab].opentype_features[tag];
-}
-
void TabBar::set_tab_language(int p_tab, const String &p_language) {
ERR_FAIL_INDEX(p_tab, tabs.size());
@@ -1553,9 +1511,6 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &TabBar::get_tab_title);
ClassDB::bind_method(D_METHOD("set_tab_text_direction", "tab_idx", "direction"), &TabBar::set_tab_text_direction);
ClassDB::bind_method(D_METHOD("get_tab_text_direction", "tab_idx"), &TabBar::get_tab_text_direction);
- ClassDB::bind_method(D_METHOD("set_tab_opentype_feature", "tab_idx", "tag", "values"), &TabBar::set_tab_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_tab_opentype_feature", "tab_idx", "tag"), &TabBar::get_tab_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_tab_opentype_features", "tab_idx"), &TabBar::clear_tab_opentype_features);
ClassDB::bind_method(D_METHOD("set_tab_language", "tab_idx", "language"), &TabBar::set_tab_language);
ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &TabBar::get_tab_language);
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabBar::set_tab_icon);
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index 548a2e62af..d123385e47 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -57,7 +57,6 @@ private:
String text;
String xl_text;
- Dictionary opentype_features;
String language;
Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED;
@@ -137,10 +136,6 @@ public:
void set_tab_text_direction(int p_tab, TextDirection p_text_direction);
TextDirection get_tab_text_direction(int p_tab) const;
- void set_tab_opentype_feature(int p_tab, const String &p_name, int p_value);
- int get_tab_opentype_feature(int p_tab, const String &p_name) const;
- void clear_tab_opentype_features(int p_tab);
-
void set_tab_language(int p_tab, const String &p_language);
String get_tab_language(int p_tab) const;
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 8299d73b68..fa929344d4 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -727,6 +727,7 @@ void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) {
if (!get_clip_tabs()) {
update_minimum_size();
}
+ call_deferred(SNAME("_repaint"));
}
bool TabContainer::is_tab_hidden(int p_tab) const {
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index d9a91590f7..9c6cd6bdb2 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -75,14 +75,6 @@ int TextEdit::Text::get_tab_size() const {
return tab_size;
}
-void TextEdit::Text::set_font_features(const Dictionary &p_features) {
- if (opentype_features.hash() == p_features.hash()) {
- return;
- }
- opentype_features = p_features;
- is_dirty = true;
-}
-
void TextEdit::Text::set_direction_and_language(TextServer::Direction p_direction, const String &p_language) {
if (direction == p_direction && language == p_language) {
return;
@@ -178,7 +170,7 @@ void TextEdit::Text::_calculate_max_line_width() {
void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_changed, const String &p_ime_text, const Array &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
- if (font.is_null() || font_size <= 0) {
+ if (font.is_null()) {
return; // Not in tree?
}
@@ -191,14 +183,14 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_chan
text.write[p_line].data_buf->set_preserve_control(draw_control_chars);
if (p_ime_text.length() > 0) {
if (p_text_changed) {
- text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
+ text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, language);
}
if (!p_bidi_override.is_empty()) {
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), p_bidi_override);
}
} else {
if (p_text_changed) {
- text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language);
+ text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, language);
}
if (!text[p_line].bidi_override.is_empty()) {
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), text[p_line].bidi_override);
@@ -209,14 +201,17 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_chan
RID r = text.write[p_line].data_buf->get_rid();
int spans = TS->shaped_get_span_count(r);
for (int i = 0; i < spans; i++) {
- TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, opentype_features);
+ TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, font->get_opentype_features());
+ }
+ for (int i = 0; i < TextServer::SPACING_MAX; i++) {
+ TS->shaped_text_set_spacing(r, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i)));
}
}
// Apply tab align.
if (tab_size > 0) {
Vector<float> tabs;
- tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
+ tabs.push_back(font->get_char_size(' ', font_size).width * tab_size);
text.write[p_line].data_buf->tab_align(tabs);
}
@@ -255,7 +250,7 @@ void TextEdit::Text::invalidate_all_lines() {
if (tab_size_dirty) {
if (tab_size > 0) {
Vector<float> tabs;
- tabs.push_back(font->get_char_size(' ', 0, font_size).width * tab_size);
+ tabs.push_back(font->get_char_size(' ', font_size).width * tab_size);
text.write[i].data_buf->tab_align(tabs);
}
// Tabs have changes, force width update.
@@ -277,7 +272,7 @@ void TextEdit::Text::invalidate_font() {
max_width = -1;
line_height = -1;
- if (!font.is_null() && font_size > 0) {
+ if (font.is_valid() && font_size > 0) {
font_height = font->get_height(font_size);
}
@@ -295,7 +290,7 @@ void TextEdit::Text::invalidate_all() {
max_width = -1;
line_height = -1;
- if (!font.is_null() && font_size > 0) {
+ if (font.is_valid() && font_size > 0) {
font_height = font->get_height(font_size);
}
@@ -973,7 +968,7 @@ void TextEdit::_notification(int p_what) {
// Give visual indication of empty selected line.
if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
- float char_w = font->get_char_size(' ', 0, font_size).width;
+ float char_w = font->get_char_size(' ', font_size).width;
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), selection_color);
} else {
@@ -1071,7 +1066,7 @@ void TextEdit::_notification(int p_what) {
// Draw line.
RID rid = ldata->get_line_rid(line_wrap_index);
- float text_height = TS->shaped_text_get_size(rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
+ float text_height = TS->shaped_text_get_size(rid).y;
if (rtl) {
char_margin = size.width - char_margin - TS->shaped_text_get_size(rid).x;
@@ -1355,7 +1350,7 @@ void TextEdit::_notification(int p_what) {
ts_caret.l_caret.size.y = caret_width;
}
if (ts_caret.l_caret.position.x >= TS->shaped_text_get_size(rid).x) {
- ts_caret.l_caret.size.x = font->get_char_size('m', 0, font_size).x;
+ ts_caret.l_caret.size.x = font->get_char_size('m', font_size).x;
} else {
ts_caret.l_caret.size.x = 3 * caret_width;
}
@@ -1723,7 +1718,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
- } else if (is_mouse_over_selection()) {
+ } else if (drag_and_drop_selection_enabled && is_mouse_over_selection()) {
selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
selection.drag_attempt = true;
} else {
@@ -2573,7 +2568,7 @@ void TextEdit::_update_placeholder() {
placeholder_data_buf->set_width(text.get_width());
placeholder_data_buf->set_direction((TextServer::Direction)text_direction);
placeholder_data_buf->set_preserve_control(draw_control_chars);
- placeholder_data_buf->add_string(placeholder_text, font, font_size, opentype_features, language);
+ placeholder_data_buf->add_string(placeholder_text, font, font_size, language);
placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_text);
if (placeholder_bidi_override.is_empty()) {
@@ -2582,7 +2577,7 @@ void TextEdit::_update_placeholder() {
if (get_tab_size() > 0) {
Vector<float> tabs;
- tabs.push_back(font->get_char_size(' ', 0, font_size).width * get_tab_size());
+ tabs.push_back(font->get_char_size(' ', font_size).width * get_tab_size());
placeholder_data_buf->tab_align(tabs);
}
@@ -2653,7 +2648,6 @@ void TextEdit::_update_caches() {
dir = (TextServer::Direction)text_direction;
}
text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
- text.set_font_features(opentype_features);
text.set_draw_control_chars(draw_control_chars);
text.set_font(font);
text.set_font_size(font_size);
@@ -2668,7 +2662,11 @@ void TextEdit::_update_caches() {
/* General overrides. */
Size2 TextEdit::get_minimum_size() const {
- return style_normal->get_minimum_size();
+ Size2 size = style_normal->get_minimum_size();
+ if (fit_content_height) {
+ size.y += content_height_cache;
+ }
+ return size;
}
bool TextEdit::is_text_field() const {
@@ -2854,33 +2852,6 @@ Control::TextDirection TextEdit::get_text_direction() const {
return text_direction;
}
-void TextEdit::set_opentype_feature(const String &p_name, int p_value) {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
- opentype_features[tag] = p_value;
- text.set_font_features(opentype_features);
- text.invalidate_font();
- _update_placeholder();
- update();
- }
-}
-
-int TextEdit::get_opentype_feature(const String &p_name) const {
- int32_t tag = TS->name_to_tag(p_name);
- if (!opentype_features.has(tag)) {
- return -1;
- }
- return opentype_features[tag];
-}
-
-void TextEdit::clear_opentype_features() {
- opentype_features.clear();
- text.set_font_features(opentype_features);
- text.invalidate_font();
- _update_placeholder();
- update();
-}
-
void TextEdit::set_language(const String &p_language) {
if (language != p_language) {
language = p_language;
@@ -4163,6 +4134,14 @@ bool TextEdit::is_deselect_on_focus_loss_enabled() const {
return deselect_on_focus_loss_enabled;
}
+void TextEdit::set_drag_and_drop_selection_enabled(const bool p_enabled) {
+ drag_and_drop_selection_enabled = p_enabled;
+}
+
+bool TextEdit::is_drag_and_drop_selection_enabled() const {
+ return drag_and_drop_selection_enabled;
+}
+
void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) {
override_selected_font_color = p_override_selected_font_color;
}
@@ -4491,6 +4470,18 @@ float TextEdit::get_v_scroll_speed() const {
return v_scroll_speed;
}
+void TextEdit::set_fit_content_height_enabled(const bool p_enabled) {
+ if (fit_content_height == p_enabled) {
+ return;
+ }
+ fit_content_height = p_enabled;
+ update_minimum_size();
+}
+
+bool TextEdit::is_fit_content_height_enabled() const {
+ return fit_content_height;
+}
+
double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
ERR_FAIL_COND_V(p_wrap_index < 0, 0);
@@ -5047,10 +5038,6 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &TextEdit::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction"), &TextEdit::get_text_direction);
- ClassDB::bind_method(D_METHOD("set_opentype_feature", "tag", "value"), &TextEdit::set_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_opentype_feature", "tag"), &TextEdit::get_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_opentype_features"), &TextEdit::clear_opentype_features);
-
ClassDB::bind_method(D_METHOD("set_language", "language"), &TextEdit::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &TextEdit::get_language);
@@ -5083,6 +5070,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_text", "text"), &TextEdit::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &TextEdit::get_text);
+
ClassDB::bind_method(D_METHOD("get_line_count"), &TextEdit::get_line_count);
ClassDB::bind_method(D_METHOD("set_placeholder", "text"), &TextEdit::set_placeholder);
@@ -5243,6 +5231,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_deselect_on_focus_loss_enabled", "enable"), &TextEdit::set_deselect_on_focus_loss_enabled);
ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &TextEdit::is_deselect_on_focus_loss_enabled);
+ ClassDB::bind_method(D_METHOD("set_drag_and_drop_selection_enabled", "enable"), &TextEdit::set_drag_and_drop_selection_enabled);
+ ClassDB::bind_method(D_METHOD("is_drag_and_drop_selection_enabled"), &TextEdit::is_drag_and_drop_selection_enabled);
+
ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color);
ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color);
@@ -5286,7 +5277,7 @@ void TextEdit::_bind_methods() {
/* Viewport. */
// Scrolling.
- ClassDB::bind_method(D_METHOD("set_smooth_scroll_enable", "enable"), &TextEdit::set_smooth_scroll_enabled);
+ ClassDB::bind_method(D_METHOD("set_smooth_scroll_enabled", "enable"), &TextEdit::set_smooth_scroll_enabled);
ClassDB::bind_method(D_METHOD("is_smooth_scroll_enabled"), &TextEdit::is_smooth_scroll_enabled);
ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &TextEdit::set_v_scroll);
@@ -5301,6 +5292,9 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_v_scroll_speed", "speed"), &TextEdit::set_v_scroll_speed);
ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed);
+ ClassDB::bind_method(D_METHOD("set_fit_content_height_enabled"), &TextEdit::set_fit_content_height_enabled);
+ ClassDB::bind_method(D_METHOD("is_fit_content_height_enabled"), &TextEdit::is_fit_content_height_enabled);
+
ClassDB::bind_method(D_METHOD("get_scroll_pos_for_line", "line", "wrap_index"), &TextEdit::get_scroll_pos_for_line, DEFVAL(0));
// Visible lines.
@@ -5397,14 +5391,13 @@ void TextEdit::_bind_methods() {
/* Inspector */
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text", PROPERTY_HINT_MULTILINE_TEXT), "set_placeholder", "get_placeholder");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_and_drop_selection_enabled"), "set_drag_and_drop_selection_enabled", "is_drag_and_drop_selection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode");
@@ -5419,11 +5412,12 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter");
ADD_GROUP("Scroll", "scroll_");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_smooth"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_smooth"), "set_smooth_scroll_enabled", "is_smooth_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_v_scroll_speed", PROPERTY_HINT_NONE, "suffix:px/s"), "set_v_scroll_speed", "get_v_scroll_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_past_end_of_file"), "set_scroll_past_end_of_file_enabled", "is_scroll_past_end_of_file_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical", PROPERTY_HINT_NONE, "suffix:px"), "set_v_scroll", "get_v_scroll");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal", PROPERTY_HINT_NONE, "suffix:px"), "set_h_scroll", "get_h_scroll");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_fit_content_height"), "set_fit_content_height_enabled", "is_fit_content_height_enabled");
ADD_GROUP("Minimap", "minimap_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_draw"), "set_draw_minimap", "is_drawing_minimap");
@@ -5436,7 +5430,9 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_move_on_right_click"), "set_move_caret_on_right_click_enabled", "is_move_caret_on_right_click_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled");
- ADD_GROUP("Structured Text", "structured_text_");
+ ADD_GROUP("BiDi", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
@@ -5461,60 +5457,6 @@ void TextEdit::_bind_methods() {
ProjectSettings::get_singleton()->set_custom_property_info("gui/common/text_edit_undo_stack_max_size", PropertyInfo(Variant::INT, "gui/common/text_edit_undo_stack_max_size", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers.
}
-bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- int value = p_value;
- if (value == -1) {
- if (opentype_features.has(tag)) {
- opentype_features.erase(tag);
- text.set_font_features(opentype_features);
- text.invalidate_font();
- _update_placeholder();
- update();
- }
- } else {
- if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
- opentype_features[tag] = value;
- text.set_font_features(opentype_features);
- text.invalidate_font();
- _update_placeholder();
- update();
- }
- }
- notify_property_list_changed();
- return true;
- }
-
- return false;
-}
-
-bool TextEdit::_get(const StringName &p_name, Variant &r_ret) const {
- String str = p_name;
- if (str.begins_with("opentype_features/")) {
- String name = str.get_slicec('/', 1);
- int32_t tag = TS->name_to_tag(name);
- if (opentype_features.has(tag)) {
- r_ret = opentype_features[tag];
- return true;
- } else {
- r_ret = -1;
- return true;
- }
- }
- return false;
-}
-
-void TextEdit::_get_property_list(List<PropertyInfo> *p_list) const {
- for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
- String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
- }
- p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
-}
-
/* Internal API for CodeEdit. */
// Line hiding.
void TextEdit::_set_hiding_enabled(bool p_enabled) {
@@ -6185,6 +6127,11 @@ void TextEdit::_update_scrollbars() {
total_width += minimap_width;
}
+ content_height_cache = MAX(total_rows, 1) * get_line_height();
+ if (fit_content_height) {
+ update_minimum_size();
+ }
+
updating_scrolls = true;
if (total_rows > visible_rows) {
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 993203bee6..6711cf8c7f 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -160,7 +160,6 @@ private:
int font_size = -1;
int font_height = 0;
- Dictionary opentype_features;
String language;
TextServer::Direction direction = TextServer::DIRECTION_AUTO;
bool draw_control_chars = false;
@@ -180,7 +179,6 @@ private:
int get_tab_size() const;
void set_font(const Ref<Font> &p_font);
void set_font_size(int p_font_size);
- void set_font_features(const Dictionary &p_features);
void set_direction_and_language(TextServer::Direction p_direction, const String &p_language);
void set_draw_control_chars(bool p_enabled);
@@ -271,7 +269,6 @@ private:
TextDirection text_direction = TEXT_DIRECTION_AUTO;
TextDirection input_direction = TEXT_DIRECTION_LTR;
- Dictionary opentype_features;
String language = "";
TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
@@ -422,6 +419,7 @@ private:
bool selecting_enabled = true;
bool deselect_on_focus_loss_enabled = true;
+ bool drag_and_drop_selection_enabled = true;
Color font_selected_color = Color(1, 1, 1);
Color selection_color = Color(1, 1, 1);
@@ -455,6 +453,8 @@ private:
HScrollBar *h_scroll = nullptr;
VScrollBar *v_scroll = nullptr;
+ float content_height_cache = 0.0;
+ bool fit_content_height = false;
bool scroll_past_end_of_file_enabled = false;
// Smooth scrolling.
@@ -578,10 +578,6 @@ protected:
static void _bind_methods();
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
/* Internal API for CodeEdit, pending public API. */
// brace matching
bool highlight_matching_braces_enabled = false;
@@ -647,10 +643,6 @@ public:
void set_text_direction(TextDirection p_text_direction);
TextDirection get_text_direction() const;
- void set_opentype_feature(const String &p_name, int p_value);
- int get_opentype_feature(const String &p_name) const;
- void clear_opentype_features();
-
void set_language(const String &p_language);
String get_language() const;
@@ -683,6 +675,7 @@ public:
void set_text(const String &p_text);
String get_text() const;
+
int get_line_count() const;
void set_placeholder(const String &p_text);
@@ -795,6 +788,9 @@ public:
void set_deselect_on_focus_loss_enabled(const bool p_enabled);
bool is_deselect_on_focus_loss_enabled() const;
+ void set_drag_and_drop_selection_enabled(const bool p_enabled);
+ bool is_drag_and_drop_selection_enabled() const;
+
void set_override_selected_font_color(bool p_override_selected_font_color);
bool is_overriding_selected_font_color() const;
@@ -847,6 +843,9 @@ public:
void set_v_scroll_speed(float p_speed);
float get_v_scroll_speed() const;
+ void set_fit_content_height_enabled(const bool p_enabled);
+ bool is_fit_content_height_enabled() const;
+
double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const;
// Visible lines.
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index d3e7540790..32d348c121 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -303,37 +303,6 @@ Control::TextDirection TreeItem::get_text_direction(int p_column) const {
return cells[p_column].text_direction;
}
-void TreeItem::clear_opentype_features(int p_column) {
- ERR_FAIL_INDEX(p_column, cells.size());
-
- cells.write[p_column].opentype_features.clear();
- cells.write[p_column].dirty = true;
- cells.write[p_column].cached_minimum_size_dirty = true;
-
- _changed_notify(p_column);
-}
-
-void TreeItem::set_opentype_feature(int p_column, const String &p_name, int p_value) {
- ERR_FAIL_INDEX(p_column, cells.size());
- int32_t tag = TS->name_to_tag(p_name);
- if (!cells[p_column].opentype_features.has(tag) || (int)cells[p_column].opentype_features[tag] != p_value) {
- cells.write[p_column].opentype_features[tag] = p_value;
- cells.write[p_column].dirty = true;
- cells.write[p_column].cached_minimum_size_dirty = true;
-
- _changed_notify(p_column);
- }
-}
-
-int TreeItem::get_opentype_feature(int p_column, const String &p_name) const {
- ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
- int32_t tag = TS->name_to_tag(p_name);
- if (!cells[p_column].opentype_features.has(tag)) {
- return -1;
- }
- return cells[p_column].opentype_features[tag];
-}
-
void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser) {
ERR_FAIL_INDEX(p_column, cells.size());
@@ -1269,10 +1238,6 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_text_direction", "column", "direction"), &TreeItem::set_text_direction);
ClassDB::bind_method(D_METHOD("get_text_direction", "column"), &TreeItem::get_text_direction);
- ClassDB::bind_method(D_METHOD("set_opentype_feature", "column", "tag", "value"), &TreeItem::set_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_opentype_feature", "column", "tag"), &TreeItem::get_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_opentype_features", "column"), &TreeItem::clear_opentype_features);
-
ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override", "column", "parser"), &TreeItem::set_structured_text_bidi_override);
ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override", "column"), &TreeItem::get_structured_text_bidi_override);
@@ -1667,7 +1632,7 @@ void Tree::update_column(int p_col) {
columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction);
}
- columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].opentype_features, !columns[p_col].language.is_empty() ? columns[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
+ columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].language);
}
void Tree::update_item_cell(TreeItem *p_item, int p_col) {
@@ -1725,7 +1690,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
} else {
font_size = cache.font_size;
}
- p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].opentype_features, !p_item->cells[p_col].language.is_empty() ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
+ p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].language);
TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext));
p_item->cells.write[p_col].dirty = false;
}
@@ -4192,7 +4157,7 @@ int Tree::get_column_minimum_width(int p_column) const {
// Check if the visible title of the column is wider.
if (show_column_titles) {
- min_width = MAX(cache.font->get_string_size(columns[p_column].title, cache.font_size).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width);
+ min_width = MAX(cache.font->get_string_size(columns[p_column].title, HORIZONTAL_ALIGNMENT_LEFT, -1, cache.font_size).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width);
}
if (!columns[p_column].clip_content) {
@@ -4471,32 +4436,6 @@ Control::TextDirection Tree::get_column_title_direction(int p_column) const {
return columns[p_column].text_direction;
}
-void Tree::clear_column_title_opentype_features(int p_column) {
- ERR_FAIL_INDEX(p_column, columns.size());
- columns.write[p_column].opentype_features.clear();
- update_column(p_column);
- update();
-}
-
-void Tree::set_column_title_opentype_feature(int p_column, const String &p_name, int p_value) {
- ERR_FAIL_INDEX(p_column, columns.size());
- int32_t tag = TS->name_to_tag(p_name);
- if (!columns[p_column].opentype_features.has(tag) || (int)columns[p_column].opentype_features[tag] != p_value) {
- columns.write[p_column].opentype_features[tag] = p_value;
- update_column(p_column);
- update();
- }
-}
-
-int Tree::get_column_title_opentype_feature(int p_column, const String &p_name) const {
- ERR_FAIL_INDEX_V(p_column, columns.size(), -1);
- int32_t tag = TS->name_to_tag(p_name);
- if (!columns[p_column].opentype_features.has(tag)) {
- return -1;
- }
- return columns[p_column].opentype_features[tag];
-}
-
void Tree::set_column_title_language(int p_column, const String &p_language) {
ERR_FAIL_INDEX(p_column, columns.size());
if (columns[p_column].language != p_language) {
@@ -4983,10 +4922,6 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_column_title_direction", "column", "direction"), &Tree::set_column_title_direction);
ClassDB::bind_method(D_METHOD("get_column_title_direction", "column"), &Tree::get_column_title_direction);
- ClassDB::bind_method(D_METHOD("set_column_title_opentype_feature", "column", "tag", "value"), &Tree::set_column_title_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_column_title_opentype_feature", "column", "tag"), &Tree::get_column_title_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_column_title_opentype_features", "column"), &Tree::clear_column_title_opentype_features);
-
ClassDB::bind_method(D_METHOD("set_column_title_language", "column", "language"), &Tree::set_column_title_language);
ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 0a8dd3204a..65f7ab185c 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -63,7 +63,6 @@ private:
String text;
String suffix;
Ref<TextLine> text_buf;
- Dictionary opentype_features;
String language;
TextServer::StructuredTextParser st_parser = TextServer::STRUCTURED_TEXT_DEFAULT;
Array st_args;
@@ -220,10 +219,6 @@ public:
void set_text_direction(int p_column, Control::TextDirection p_text_direction);
Control::TextDirection get_text_direction(int p_column) const;
- void set_opentype_feature(int p_column, const String &p_name, int p_value);
- int get_opentype_feature(int p_column, const String &p_name) const;
- void clear_opentype_features(int p_column);
-
void set_structured_text_bidi_override(int p_column, TextServer::StructuredTextParser p_parser);
TextServer::StructuredTextParser get_structured_text_bidi_override(int p_column) const;
@@ -429,7 +424,6 @@ private:
bool clip_content = false;
String title;
Ref<TextLine> text_buf;
- Dictionary opentype_features;
String language;
Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED;
ColumnInfo() {
@@ -666,10 +660,6 @@ public:
void set_column_title_direction(int p_column, Control::TextDirection p_text_direction);
Control::TextDirection get_column_title_direction(int p_column) const;
- void set_column_title_opentype_feature(int p_column, const String &p_name, int p_value);
- int get_column_title_opentype_feature(int p_column, const String &p_name) const;
- void clear_column_title_opentype_features(int p_column);
-
void set_column_title_language(int p_column, const String &p_language);
String get_column_title_language(int p_column) const;
diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp
index 122e36904b..86334882fa 100644
--- a/scene/gui/video_stream_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -174,6 +174,28 @@ void VideoStreamPlayer::_notification(int p_notification) {
Size2 s = expand ? get_size() : texture->get_size();
draw_texture_rect(texture, Rect2(Point2(), s), false);
} break;
+
+ case NOTIFICATION_PAUSED: {
+ if (is_playing() && !is_paused()) {
+ paused_from_tree = true;
+ if (playback.is_valid()) {
+ playback->set_paused(true);
+ set_process_internal(false);
+ }
+ last_audio_time = 0;
+ }
+ } break;
+
+ case NOTIFICATION_UNPAUSED: {
+ if (paused_from_tree) {
+ paused_from_tree = false;
+ if (playback.is_valid()) {
+ playback->set_paused(false);
+ set_process_internal(true);
+ }
+ last_audio_time = 0;
+ }
+ } break;
}
}
@@ -255,6 +277,10 @@ void VideoStreamPlayer::play() {
playback->play();
set_process_internal(true);
last_audio_time = 0;
+
+ if (!can_process()) {
+ _notification(NOTIFICATION_PAUSED);
+ }
}
void VideoStreamPlayer::stop() {
@@ -281,6 +307,14 @@ bool VideoStreamPlayer::is_playing() const {
void VideoStreamPlayer::set_paused(bool p_paused) {
paused = p_paused;
+ if (!p_paused && !can_process()) {
+ paused_from_tree = true;
+ return;
+ } else if (p_paused && paused_from_tree) {
+ paused_from_tree = false;
+ return;
+ }
+
if (playback.is_valid()) {
playback->set_paused(p_paused);
set_process_internal(!p_paused);
diff --git a/scene/gui/video_stream_player.h b/scene/gui/video_stream_player.h
index 130b2901f1..913e7905b6 100644
--- a/scene/gui/video_stream_player.h
+++ b/scene/gui/video_stream_player.h
@@ -60,10 +60,11 @@ class VideoStreamPlayer : public Control {
int wait_resampler_limit = 2;
bool paused = false;
+ bool paused_from_tree = false;
bool autoplay = false;
float volume = 1.0;
double last_audio_time = 0.0;
- bool expand = true;
+ bool expand = false;
bool loops = false;
int buffering_ms = 500;
int audio_track = 0;