feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
Size2 AspectRatioContainer::get_minimum_size() const {
|
||||
Size2 ms;
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@
|
|||
#include "base_button.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/main/window.h"
|
||||
|
||||
void BaseButton::_unpress_group() {
|
||||
if (!button_group.is_valid()) {
|
||||
if (button_group.is_null()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +114,11 @@ void BaseButton::_notification(int p_what) {
|
|||
} else if (status.hovering) {
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
if (status.pressed_down_with_focus) {
|
||||
status.pressed_down_with_focus = false;
|
||||
emit_signal(SNAME("button_up"));
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED:
|
||||
|
|
@ -140,14 +145,19 @@ void BaseButton::_pressed() {
|
|||
void BaseButton::_toggled(bool p_pressed) {
|
||||
GDVIRTUAL_CALL(_toggled, p_pressed);
|
||||
toggled(p_pressed);
|
||||
emit_signal(SNAME("toggled"), p_pressed);
|
||||
emit_signal(SceneStringName(toggled), p_pressed);
|
||||
}
|
||||
|
||||
void BaseButton::on_action_event(Ref<InputEvent> p_event) {
|
||||
if (p_event->is_pressed()) {
|
||||
Ref<InputEventMouseButton> mouse_button = p_event;
|
||||
|
||||
if (p_event->is_pressed() && (mouse_button.is_null() || status.hovering)) {
|
||||
status.press_attempt = true;
|
||||
status.pressing_inside = true;
|
||||
emit_signal(SNAME("button_down"));
|
||||
if (!status.pressed_down_with_focus) {
|
||||
status.pressed_down_with_focus = true;
|
||||
emit_signal(SNAME("button_down"));
|
||||
}
|
||||
}
|
||||
|
||||
if (status.press_attempt && status.pressing_inside) {
|
||||
|
|
@ -174,15 +184,12 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
|
|||
}
|
||||
|
||||
if (!p_event->is_pressed()) {
|
||||
Ref<InputEventMouseButton> mouse_button = p_event;
|
||||
if (mouse_button.is_valid()) {
|
||||
if (!has_point(mouse_button->get_position())) {
|
||||
status.hovering = false;
|
||||
}
|
||||
}
|
||||
status.press_attempt = false;
|
||||
status.pressing_inside = false;
|
||||
emit_signal(SNAME("button_up"));
|
||||
if (status.pressed_down_with_focus) {
|
||||
status.pressed_down_with_focus = false;
|
||||
emit_signal(SNAME("button_up"));
|
||||
}
|
||||
}
|
||||
|
||||
queue_redraw();
|
||||
|
|
@ -208,6 +215,7 @@ void BaseButton::set_disabled(bool p_disabled) {
|
|||
status.pressing_inside = false;
|
||||
}
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
}
|
||||
|
||||
bool BaseButton::is_disabled() const {
|
||||
|
|
@ -394,16 +402,31 @@ void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
|
||||
String BaseButton::get_tooltip(const Point2 &p_pos) const {
|
||||
String tooltip = Control::get_tooltip(p_pos);
|
||||
if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->has_valid_event()) {
|
||||
String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")";
|
||||
if (!tooltip.is_empty() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
|
||||
text += "\n" + atr(tooltip);
|
||||
}
|
||||
tooltip = text;
|
||||
Control *BaseButton::make_custom_tooltip(const String &p_text) const {
|
||||
Control *control = Control::make_custom_tooltip(p_text);
|
||||
if (control) {
|
||||
return control;
|
||||
}
|
||||
return tooltip;
|
||||
if (!shortcut_in_tooltip || shortcut.is_null() || !shortcut->has_valid_event()) {
|
||||
return nullptr; // Use the default tooltip label.
|
||||
}
|
||||
|
||||
String text = atr(shortcut->get_name()) + " (" + shortcut->get_as_text() + ")";
|
||||
if (!p_text.is_empty() && shortcut->get_name().nocasecmp_to(p_text) != 0) {
|
||||
text += "\n" + atr(p_text);
|
||||
}
|
||||
|
||||
// Make a label similar to the default tooltip label.
|
||||
// Auto translation is disabled because we already did that manually above.
|
||||
//
|
||||
// We can't customize the tooltip text by overriding `get_tooltip()`
|
||||
// because otherwise user-defined `_make_custom_tooltip()` would receive
|
||||
// the translated and annotated text.
|
||||
Label *label = memnew(Label(text));
|
||||
label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
label->set_theme_type_variation(SNAME("TooltipLabel"));
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
void BaseButton::set_button_group(const Ref<ButtonGroup> &p_group) {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ private:
|
|||
bool hovering = false;
|
||||
bool press_attempt = false;
|
||||
bool pressing_inside = false;
|
||||
|
||||
bool pressed_down_with_focus = false;
|
||||
bool disabled = false;
|
||||
|
||||
} status;
|
||||
|
|
@ -134,7 +134,7 @@ public:
|
|||
void set_shortcut(const Ref<Shortcut> &p_shortcut);
|
||||
Ref<Shortcut> get_shortcut() const;
|
||||
|
||||
virtual String get_tooltip(const Point2 &p_pos) const override;
|
||||
virtual Control *make_custom_tooltip(const String &p_text) const override;
|
||||
|
||||
void set_button_group(const Ref<ButtonGroup> &p_group);
|
||||
Ref<ButtonGroup> get_button_group() const;
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ Size2 BoxContainer::get_minimum_size() const {
|
|||
bool first = true;
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,7 @@
|
|||
|
||||
#include "button.h"
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
Size2 Button::get_minimum_size() const {
|
||||
Ref<Texture2D> _icon = icon;
|
||||
|
|
@ -212,6 +210,13 @@ void Button::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
// Reshape and update size min. if text is invalidated by an external source (e.g., oversampling).
|
||||
if (text_buf.is_valid() && !TS->shaped_text_is_ready(text_buf->get_rid())) {
|
||||
_shape();
|
||||
|
||||
update_minimum_size();
|
||||
}
|
||||
|
||||
const RID ci = get_canvas_item();
|
||||
const Size2 size = get_size();
|
||||
|
||||
|
|
@ -298,19 +303,12 @@ void Button::_notification(int p_what) {
|
|||
}
|
||||
} break;
|
||||
case DRAW_HOVER_PRESSED: {
|
||||
// Edge case for CheckButton and CheckBox.
|
||||
if (has_theme_stylebox("hover_pressed")) {
|
||||
if (has_theme_color(SNAME("font_hover_pressed_color"))) {
|
||||
font_color = theme_cache.font_hover_pressed_color;
|
||||
}
|
||||
if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
|
||||
icon_modulate_color = theme_cache.icon_hover_pressed_color;
|
||||
}
|
||||
|
||||
break;
|
||||
font_color = theme_cache.font_hover_pressed_color;
|
||||
if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
|
||||
icon_modulate_color = theme_cache.icon_hover_pressed_color;
|
||||
}
|
||||
}
|
||||
[[fallthrough]];
|
||||
|
||||
} break;
|
||||
case DRAW_PRESSED: {
|
||||
if (has_theme_color(SNAME("font_pressed_color"))) {
|
||||
font_color = theme_cache.font_pressed_color;
|
||||
|
|
@ -437,6 +435,9 @@ void Button::_notification(int p_what) {
|
|||
text_buf->set_alignment(align_rtl_checked);
|
||||
|
||||
float text_buf_width = Math::ceil(MAX(1.0f, drawable_size_remained.width)); // The space's width filled by the text_buf.
|
||||
if (autowrap_mode != TextServer::AUTOWRAP_OFF && !Math::is_equal_approx(text_buf_width, text_buf->get_width())) {
|
||||
update_minimum_size();
|
||||
}
|
||||
text_buf->set_width(text_buf_width);
|
||||
|
||||
Point2 text_ofs;
|
||||
|
|
@ -494,7 +495,7 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
|
|||
paragraph = text_buf;
|
||||
} else {
|
||||
paragraph.instantiate();
|
||||
const_cast<Button *>(this)->_shape(paragraph, p_text);
|
||||
_shape(paragraph, p_text);
|
||||
}
|
||||
|
||||
Size2 minsize = paragraph->get_size();
|
||||
|
|
@ -533,7 +534,7 @@ Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Textu
|
|||
return (theme_cache.align_to_largest_stylebox ? _get_largest_stylebox_size() : _get_current_stylebox()->get_minimum_size()) + minsize;
|
||||
}
|
||||
|
||||
void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) {
|
||||
void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) const {
|
||||
if (p_paragraph.is_null()) {
|
||||
p_paragraph = text_buf;
|
||||
}
|
||||
|
|
@ -567,6 +568,7 @@ void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) {
|
|||
}
|
||||
autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
|
||||
p_paragraph->set_break_flags(autowrap_flags);
|
||||
p_paragraph->set_line_spacing(theme_cache.line_spacing);
|
||||
|
||||
if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
|
||||
p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
|
||||
|
|
@ -648,7 +650,7 @@ String Button::get_language() const {
|
|||
return language;
|
||||
}
|
||||
|
||||
void Button::set_icon(const Ref<Texture2D> &p_icon) {
|
||||
void Button::set_button_icon(const Ref<Texture2D> &p_icon) {
|
||||
if (icon == p_icon) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -672,7 +674,7 @@ void Button::_texture_changed() {
|
|||
update_minimum_size();
|
||||
}
|
||||
|
||||
Ref<Texture2D> Button::get_icon() const {
|
||||
Ref<Texture2D> Button::get_button_icon() const {
|
||||
return icon;
|
||||
}
|
||||
|
||||
|
|
@ -768,8 +770,8 @@ void Button::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_text_direction"), &Button::get_text_direction);
|
||||
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);
|
||||
ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon);
|
||||
ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_button_icon);
|
||||
ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_button_icon);
|
||||
ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat);
|
||||
ClassDB::bind_method(D_METHOD("is_flat"), &Button::is_flat);
|
||||
ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text);
|
||||
|
|
@ -839,6 +841,7 @@ void Button::_bind_methods() {
|
|||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, icon_max_width);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, align_to_largest_stylebox);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, line_spacing);
|
||||
}
|
||||
|
||||
Button::Button(const String &p_text) {
|
||||
|
|
|
|||
|
|
@ -100,9 +100,10 @@ private:
|
|||
|
||||
int h_separation = 0;
|
||||
int icon_max_width = 0;
|
||||
int line_spacing = 0;
|
||||
} theme_cache;
|
||||
|
||||
void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = "");
|
||||
void _shape(Ref<TextParagraph> p_paragraph = Ref<TextParagraph>(), String p_text = "") const;
|
||||
void _texture_changed();
|
||||
|
||||
protected:
|
||||
|
|
@ -137,8 +138,8 @@ public:
|
|||
void set_language(const String &p_language);
|
||||
String get_language() const;
|
||||
|
||||
void set_icon(const Ref<Texture2D> &p_icon);
|
||||
Ref<Texture2D> get_icon() const;
|
||||
void set_button_icon(const Ref<Texture2D> &p_icon);
|
||||
Ref<Texture2D> get_button_icon() const;
|
||||
|
||||
void set_expand_icon(bool p_enabled);
|
||||
bool is_expand_icon() const;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Size2 CenterContainer::get_minimum_size() const {
|
|||
}
|
||||
Size2 ms;
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,32 +31,31 @@
|
|||
#include "check_box.h"
|
||||
|
||||
#include "scene/theme/theme_db.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
Size2 CheckBox::get_icon_size() const {
|
||||
Size2 tex_size = Size2(0, 0);
|
||||
if (!theme_cache.checked.is_null()) {
|
||||
if (theme_cache.checked.is_valid()) {
|
||||
tex_size = theme_cache.checked->get_size();
|
||||
}
|
||||
if (!theme_cache.unchecked.is_null()) {
|
||||
if (theme_cache.unchecked.is_valid()) {
|
||||
tex_size = tex_size.max(theme_cache.unchecked->get_size());
|
||||
}
|
||||
if (!theme_cache.radio_checked.is_null()) {
|
||||
if (theme_cache.radio_checked.is_valid()) {
|
||||
tex_size = tex_size.max(theme_cache.radio_checked->get_size());
|
||||
}
|
||||
if (!theme_cache.radio_unchecked.is_null()) {
|
||||
if (theme_cache.radio_unchecked.is_valid()) {
|
||||
tex_size = tex_size.max(theme_cache.radio_unchecked->get_size());
|
||||
}
|
||||
if (!theme_cache.checked_disabled.is_null()) {
|
||||
if (theme_cache.checked_disabled.is_valid()) {
|
||||
tex_size = tex_size.max(theme_cache.checked_disabled->get_size());
|
||||
}
|
||||
if (!theme_cache.unchecked_disabled.is_null()) {
|
||||
if (theme_cache.unchecked_disabled.is_valid()) {
|
||||
tex_size = tex_size.max(theme_cache.unchecked_disabled->get_size());
|
||||
}
|
||||
if (!theme_cache.radio_checked_disabled.is_null()) {
|
||||
if (theme_cache.radio_checked_disabled.is_valid()) {
|
||||
tex_size = tex_size.max(theme_cache.radio_checked_disabled->get_size());
|
||||
}
|
||||
if (!theme_cache.radio_unchecked_disabled.is_null()) {
|
||||
if (theme_cache.radio_unchecked_disabled.is_valid()) {
|
||||
tex_size = tex_size.max(theme_cache.radio_unchecked_disabled->get_size());
|
||||
}
|
||||
return _fit_icon_size(tex_size);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#include "check_button.h"
|
||||
|
||||
#include "scene/theme/theme_db.h"
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
Size2 CheckButton::get_icon_size() const {
|
||||
Ref<Texture2D> on_tex;
|
||||
|
|
@ -56,10 +55,10 @@ Size2 CheckButton::get_icon_size() const {
|
|||
}
|
||||
|
||||
Size2 tex_size = Size2(0, 0);
|
||||
if (!on_tex.is_null()) {
|
||||
if (on_tex.is_valid()) {
|
||||
tex_size = on_tex->get_size();
|
||||
}
|
||||
if (!off_tex.is_null()) {
|
||||
if (off_tex.is_valid()) {
|
||||
tex_size = tex_size.max(off_tex->get_size());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,17 +31,41 @@
|
|||
#include "code_edit.h"
|
||||
#include "code_edit.compat.inc"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/string/string_builder.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
void CodeEdit::_apply_project_settings() {
|
||||
symbol_tooltip_timer->set_wait_time(GLOBAL_GET("gui/timers/tooltip_delay_sec"));
|
||||
}
|
||||
|
||||
void CodeEdit::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
_apply_project_settings();
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CodeEdit::_apply_project_settings));
|
||||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
set_gutter_width(main_gutter, get_line_height());
|
||||
set_gutter_width(line_number_gutter, (line_number_digits + 1) * theme_cache.font->get_char_size('0', theme_cache.font_size).width);
|
||||
_update_line_number_gutter_width();
|
||||
set_gutter_width(fold_gutter, get_line_height() / 1.2);
|
||||
_clear_line_number_text_cache();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSLATION_CHANGED:
|
||||
[[fallthrough]];
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
[[fallthrough]];
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
// Avoid having many hidden text editors with unused cache filling up memory.
|
||||
_clear_line_number_text_cache();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
|
|
@ -54,10 +78,10 @@ void CodeEdit::_notification(int p_what) {
|
|||
if (line_length_guideline_columns.size() > 0) {
|
||||
const int xmargin_beg = theme_cache.style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width();
|
||||
const int xmargin_end = size.width - theme_cache.style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0);
|
||||
const float char_size = theme_cache.font->get_char_size('0', theme_cache.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();
|
||||
const int column_pos = theme_cache.font->get_string_size(String("0").repeat((int)line_length_guideline_columns[i]), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).x;
|
||||
const int xoffset = xmargin_beg + column_pos - get_h_scroll();
|
||||
if (xoffset > xmargin_beg && xoffset < xmargin_end) {
|
||||
Color guideline_color = (i == 0) ? theme_cache.line_length_guideline_color : theme_cache.line_length_guideline_color * Color(1, 1, 1, 0.5);
|
||||
if (rtl) {
|
||||
|
|
@ -211,7 +235,13 @@ void CodeEdit::_notification(int p_what) {
|
|||
tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
|
||||
} else {
|
||||
if (code_completion_options[l].default_value.get_type() == Variant::COLOR) {
|
||||
draw_rect(Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value);
|
||||
const Color color = code_completion_options[l].default_value;
|
||||
const Rect2 rect = Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size);
|
||||
if (color.a < 1.0) {
|
||||
draw_texture_rect(theme_cache.completion_color_bg, rect, true);
|
||||
}
|
||||
|
||||
draw_rect(rect, color);
|
||||
}
|
||||
tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
}
|
||||
|
|
@ -245,7 +275,7 @@ void CodeEdit::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_MOUSE_EXIT: {
|
||||
queue_redraw();
|
||||
symbol_tooltip_timer->stop();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -261,15 +291,17 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
|||
code_completion_force_item_center = -1;
|
||||
queue_redraw();
|
||||
}
|
||||
code_completion_pan_offset += 1.0f;
|
||||
code_completion_pan_offset = 0;
|
||||
} else if (code_completion_pan_offset >= +1.0) {
|
||||
if (code_completion_current_selected < code_completion_options.size() - 1) {
|
||||
code_completion_current_selected++;
|
||||
code_completion_force_item_center = -1;
|
||||
queue_redraw();
|
||||
}
|
||||
code_completion_pan_offset -= 1.0f;
|
||||
code_completion_pan_offset = 0;
|
||||
}
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseButton> mb = p_gui_input;
|
||||
|
|
@ -388,7 +420,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
|||
mpos.x = get_size().x - mpos.x;
|
||||
}
|
||||
|
||||
Point2i pos = get_line_column_at_pos(mpos, false);
|
||||
Point2i pos = get_line_column_at_pos(mpos, false, false);
|
||||
int line = pos.y;
|
||||
int col = pos.x;
|
||||
|
||||
|
|
@ -410,16 +442,22 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
|||
|
||||
if (symbol_lookup_on_click_enabled) {
|
||||
if (mm->is_command_or_control_pressed() && mm->get_button_mask().is_empty()) {
|
||||
symbol_lookup_pos = get_line_column_at_pos(mpos);
|
||||
symbol_lookup_pos = get_line_column_at_pos(mpos, false, false);
|
||||
symbol_lookup_new_word = get_word_at_pos(mpos);
|
||||
if (symbol_lookup_new_word != symbol_lookup_word) {
|
||||
emit_signal(SNAME("symbol_validate"), symbol_lookup_new_word);
|
||||
}
|
||||
} else if (!mm->is_command_or_control_pressed() || (!mm->get_button_mask().is_empty() && symbol_lookup_pos != get_line_column_at_pos(mpos))) {
|
||||
} else if (!mm->is_command_or_control_pressed() || (!mm->get_button_mask().is_empty() && symbol_lookup_pos != get_line_column_at_pos(mpos, false, false))) {
|
||||
set_symbol_lookup_word_as_valid(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol_tooltip_on_hover_enabled) {
|
||||
symbol_tooltip_pos = get_line_column_at_pos(mpos, false, false);
|
||||
symbol_tooltip_word = get_word_at_pos(mpos);
|
||||
symbol_tooltip_timer->start();
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -447,10 +485,10 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
|||
}
|
||||
|
||||
bool update_code_completion = false;
|
||||
if (!k.is_valid()) {
|
||||
if (k.is_null()) {
|
||||
// MouseMotion events should not be handled by TextEdit logic if we're
|
||||
// currently clicking and dragging from the code completion panel.
|
||||
if (!mm.is_valid() || !is_code_completion_drag_started) {
|
||||
if (mm.is_null() || !is_code_completion_drag_started) {
|
||||
TextEdit::gui_input(p_gui_input);
|
||||
}
|
||||
return;
|
||||
|
|
@ -554,10 +592,6 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
|||
} else {
|
||||
update_code_completion = (allow_unicode_handling && k->get_unicode() >= 32);
|
||||
}
|
||||
|
||||
if (!update_code_completion) {
|
||||
cancel_code_completion();
|
||||
}
|
||||
}
|
||||
|
||||
/* MISC */
|
||||
|
|
@ -626,6 +660,10 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
|
|||
return CURSOR_POINTING_HAND;
|
||||
}
|
||||
|
||||
if (is_dragging_cursor()) {
|
||||
return TextEdit::get_cursor_shape(p_pos);
|
||||
}
|
||||
|
||||
if ((code_completion_active && code_completion_rect.has_point(p_pos)) || (!is_editable() && (!is_selecting_enabled() || get_line_count() == 0))) {
|
||||
return CURSOR_ARROW;
|
||||
}
|
||||
|
|
@ -765,7 +803,7 @@ void CodeEdit::_backspace_internal(int p_caret) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (to_line > 0 && _is_line_hidden(to_line - 1)) {
|
||||
if (to_line > 0 && to_column == 0 && _is_line_hidden(to_line - 1)) {
|
||||
unfold_line(to_line - 1);
|
||||
}
|
||||
|
||||
|
|
@ -815,6 +853,9 @@ void CodeEdit::_cut_internal(int p_caret) {
|
|||
delete_selection(p_caret);
|
||||
return;
|
||||
}
|
||||
if (!is_empty_selection_clipboard_enabled()) {
|
||||
return;
|
||||
}
|
||||
if (p_caret == -1) {
|
||||
delete_lines();
|
||||
} else {
|
||||
|
|
@ -1281,9 +1322,9 @@ bool CodeEdit::is_drawing_executing_lines_gutter() const {
|
|||
}
|
||||
|
||||
void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
|
||||
bool hovering = get_hovered_gutter() == Vector2i(main_gutter, p_line);
|
||||
if (draw_breakpoints && theme_cache.breakpoint_icon.is_valid()) {
|
||||
bool breakpointed = is_line_breakpointed(p_line);
|
||||
bool hovering = p_region.has_point(get_local_mouse_pos());
|
||||
bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT);
|
||||
|
||||
if (breakpointed || (hovering && !is_dragging_cursor() && !shift_pressed)) {
|
||||
|
|
@ -1302,7 +1343,6 @@ void CodeEdit::_main_gutter_draw_callback(int p_line, int p_gutter, const Rect2
|
|||
|
||||
if (draw_bookmarks && theme_cache.bookmark_icon.is_valid()) {
|
||||
bool bookmarked = is_line_bookmarked(p_line);
|
||||
bool hovering = p_region.has_point(get_local_mouse_pos());
|
||||
bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT);
|
||||
|
||||
if (bookmarked || (hovering && !is_dragging_cursor() && shift_pressed)) {
|
||||
|
|
@ -1436,7 +1476,13 @@ bool CodeEdit::is_draw_line_numbers_enabled() const {
|
|||
}
|
||||
|
||||
void CodeEdit::set_line_numbers_zero_padded(bool p_zero_padded) {
|
||||
p_zero_padded ? line_number_padding = "0" : line_number_padding = " ";
|
||||
String new_line_number_padding = p_zero_padded ? "0" : " ";
|
||||
if (line_number_padding == new_line_number_padding) {
|
||||
return;
|
||||
}
|
||||
|
||||
line_number_padding = new_line_number_padding;
|
||||
_clear_line_number_text_cache();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -1445,19 +1491,55 @@ bool CodeEdit::is_line_numbers_zero_padded() const {
|
|||
}
|
||||
|
||||
void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region) {
|
||||
String fc = String::num(p_line + 1).lpad(line_number_digits, line_number_padding);
|
||||
if (is_localizing_numeral_system()) {
|
||||
fc = TS->format_number(fc);
|
||||
if (!Rect2(Vector2(0, 0), get_size()).intersects(p_region)) {
|
||||
return;
|
||||
}
|
||||
Ref<TextLine> tl;
|
||||
tl.instantiate();
|
||||
tl->add_string(fc, theme_cache.font, theme_cache.font_size);
|
||||
int yofs = p_region.position.y + (get_line_height() - tl->get_size().y) / 2;
|
||||
|
||||
bool rtl = is_layout_rtl();
|
||||
HashMap<int, RID>::Iterator E = line_number_text_cache.find(p_line);
|
||||
RID text_rid;
|
||||
if (E) {
|
||||
text_rid = E->value;
|
||||
} else {
|
||||
String fc = String::num_int64(p_line + 1).lpad(line_number_digits, line_number_padding);
|
||||
if (is_localizing_numeral_system()) {
|
||||
fc = TS->format_number(fc);
|
||||
}
|
||||
|
||||
text_rid = TS->create_shaped_text();
|
||||
if (theme_cache.font.is_valid()) {
|
||||
TS->shaped_text_add_string(text_rid, fc, theme_cache.font->get_rids(), theme_cache.font_size, theme_cache.font->get_opentype_features());
|
||||
}
|
||||
line_number_text_cache.insert(p_line, text_rid);
|
||||
}
|
||||
|
||||
Size2 text_size = TS->shaped_text_get_size(text_rid);
|
||||
Point2 ofs = p_region.get_center() - text_size / 2;
|
||||
ofs.y += TS->shaped_text_get_ascent(text_rid);
|
||||
|
||||
if (rtl) {
|
||||
ofs.x = p_region.get_end().x - text_size.width;
|
||||
} else {
|
||||
ofs.x = p_region.position.x;
|
||||
}
|
||||
|
||||
Color number_color = get_line_gutter_item_color(p_line, line_number_gutter);
|
||||
if (number_color == Color(1, 1, 1)) {
|
||||
number_color = theme_cache.line_number_color;
|
||||
}
|
||||
tl->draw(get_canvas_item(), Point2(p_region.position.x, yofs), number_color);
|
||||
|
||||
TS->shaped_text_draw(text_rid, get_canvas_item(), ofs, -1, -1, number_color);
|
||||
}
|
||||
|
||||
void CodeEdit::_clear_line_number_text_cache() {
|
||||
for (const KeyValue<int, RID> &KV : line_number_text_cache) {
|
||||
TS->free_rid(KV.value);
|
||||
}
|
||||
line_number_text_cache.clear();
|
||||
}
|
||||
|
||||
void CodeEdit::_update_line_number_gutter_width() {
|
||||
set_gutter_width(line_number_gutter, (line_number_digits + 1) * theme_cache.font->get_char_size('0', theme_cache.font_size).width);
|
||||
}
|
||||
|
||||
/* Fold Gutter */
|
||||
|
|
@ -1977,12 +2059,18 @@ Point2 CodeEdit::get_delimiter_end_position(int p_line, int p_column) const {
|
|||
|
||||
/* Code hint */
|
||||
void CodeEdit::set_code_hint(const String &p_hint) {
|
||||
if (code_hint == p_hint) {
|
||||
return;
|
||||
}
|
||||
code_hint = p_hint;
|
||||
code_hint_xpos = -0xFFFF;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void CodeEdit::set_code_hint_draw_below(bool p_below) {
|
||||
if (code_hint_draw_below == p_below) {
|
||||
return;
|
||||
}
|
||||
code_hint_draw_below = p_below;
|
||||
queue_redraw();
|
||||
}
|
||||
|
|
@ -2303,7 +2391,7 @@ bool CodeEdit::is_symbol_lookup_on_click_enabled() const {
|
|||
|
||||
String CodeEdit::get_text_for_symbol_lookup() const {
|
||||
Point2i mp = get_local_mouse_pos();
|
||||
Point2i pos = get_line_column_at_pos(mp, false);
|
||||
Point2i pos = get_line_column_at_pos(mp, false, false);
|
||||
int line = pos.y;
|
||||
int col = pos.x;
|
||||
|
||||
|
|
@ -2344,6 +2432,26 @@ void CodeEdit::set_symbol_lookup_word_as_valid(bool p_valid) {
|
|||
}
|
||||
}
|
||||
|
||||
/* Symbol tooltip */
|
||||
void CodeEdit::set_symbol_tooltip_on_hover_enabled(bool p_enabled) {
|
||||
symbol_tooltip_on_hover_enabled = p_enabled;
|
||||
if (!p_enabled) {
|
||||
symbol_tooltip_timer->stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool CodeEdit::is_symbol_tooltip_on_hover_enabled() const {
|
||||
return symbol_tooltip_on_hover_enabled;
|
||||
}
|
||||
|
||||
void CodeEdit::_on_symbol_tooltip_timer_timeout() {
|
||||
const int line = symbol_tooltip_pos.y;
|
||||
const int column = symbol_tooltip_pos.x;
|
||||
if (line >= 0 && column >= 0 && !symbol_tooltip_word.is_empty() && !has_selection() && !Input::get_singleton()->is_anything_pressed()) {
|
||||
emit_signal(SNAME("symbol_hovered"), symbol_tooltip_word, line, column);
|
||||
}
|
||||
}
|
||||
|
||||
/* Text manipulation */
|
||||
void CodeEdit::move_lines_up() {
|
||||
begin_complex_operation();
|
||||
|
|
@ -2707,6 +2815,10 @@ void CodeEdit::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("set_symbol_lookup_word_as_valid", "valid"), &CodeEdit::set_symbol_lookup_word_as_valid);
|
||||
|
||||
/* Symbol tooltip */
|
||||
ClassDB::bind_method(D_METHOD("set_symbol_tooltip_on_hover_enabled", "enable"), &CodeEdit::set_symbol_tooltip_on_hover_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_symbol_tooltip_on_hover_enabled"), &CodeEdit::is_symbol_tooltip_on_hover_enabled);
|
||||
|
||||
/* Text manipulation */
|
||||
ClassDB::bind_method(D_METHOD("move_lines_up"), &CodeEdit::move_lines_up);
|
||||
ClassDB::bind_method(D_METHOD("move_lines_down"), &CodeEdit::move_lines_down);
|
||||
|
|
@ -2716,6 +2828,7 @@ void CodeEdit::_bind_methods() {
|
|||
|
||||
/* Inspector */
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "symbol_lookup_on_click"), "set_symbol_lookup_on_click_enabled", "is_symbol_lookup_on_click_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "symbol_tooltip_on_hover"), "set_symbol_tooltip_on_hover_enabled", "is_symbol_tooltip_on_hover_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "line_folding"), "set_line_folding_enabled", "is_line_folding_enabled");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "line_length_guidelines"), "set_line_length_guidelines", "get_line_length_guidelines");
|
||||
|
|
@ -2762,6 +2875,9 @@ void CodeEdit::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo("symbol_lookup", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "column")));
|
||||
ADD_SIGNAL(MethodInfo("symbol_validate", PropertyInfo(Variant::STRING, "symbol")));
|
||||
|
||||
/* Symbol tooltip */
|
||||
ADD_SIGNAL(MethodInfo("symbol_hovered", PropertyInfo(Variant::STRING, "symbol"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::INT, "column")));
|
||||
|
||||
/* Theme items */
|
||||
/* Gutters */
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, code_folding_color);
|
||||
|
|
@ -2771,6 +2887,7 @@ void CodeEdit::_bind_methods() {
|
|||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, can_fold_code_region_icon, "can_fold_code_region");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, folded_code_region_icon, "folded_code_region");
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CodeEdit, folded_eol_icon);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CodeEdit, completion_color_bg);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CodeEdit, breakpoint_color);
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, CodeEdit, breakpoint_icon, "breakpoint");
|
||||
|
|
@ -3476,13 +3593,13 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
|
|||
|
||||
for (int i = 1; *string_to_complete_char_lower && (all_possible_subsequence_matches.size() > 0); i++, string_to_complete_char_lower++) {
|
||||
// find all occurrences of ssq_lower to avoid looking everywhere each time
|
||||
Vector<int> all_ocurence;
|
||||
Vector<int> all_occurrences;
|
||||
if (long_option) {
|
||||
all_ocurence.push_back(target_lower.find_char(*string_to_complete_char_lower));
|
||||
all_occurrences.push_back(target_lower.find_char(*string_to_complete_char_lower));
|
||||
} else {
|
||||
for (int j = i; j < target_lower.length(); j++) {
|
||||
if (target_lower[j] == *string_to_complete_char_lower) {
|
||||
all_ocurence.push_back(j);
|
||||
all_occurrences.push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3500,7 +3617,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
for (int index : all_ocurence) {
|
||||
for (int index : all_occurrences) {
|
||||
if (index > next_index) {
|
||||
Vector<Pair<int, int>> new_match = subsequence_match;
|
||||
new_match.push_back({ index, 1 });
|
||||
|
|
@ -3602,16 +3719,13 @@ void CodeEdit::_text_changed() {
|
|||
}
|
||||
|
||||
int lc = get_line_count();
|
||||
line_number_digits = 1;
|
||||
while (lc /= 10) {
|
||||
line_number_digits++;
|
||||
int new_line_number_digits = log10l(lc) + 1;
|
||||
if (line_number_digits != new_line_number_digits) {
|
||||
_clear_line_number_text_cache();
|
||||
}
|
||||
line_number_digits = new_line_number_digits;
|
||||
_update_line_number_gutter_width();
|
||||
|
||||
if (theme_cache.font.is_valid()) {
|
||||
set_gutter_width(line_number_gutter, (line_number_digits + 1) * theme_cache.font->get_char_size('0', theme_cache.font_size).width);
|
||||
}
|
||||
|
||||
lc = get_line_count();
|
||||
List<int> breakpoints;
|
||||
for (const KeyValue<int, bool> &E : breakpointed_lines) {
|
||||
breakpoints.push_back(E.key);
|
||||
|
|
@ -3687,6 +3801,13 @@ CodeEdit::CodeEdit() {
|
|||
set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_fold_gutter_draw_callback));
|
||||
gutter_idx++;
|
||||
|
||||
/* Symbol tooltip */
|
||||
symbol_tooltip_timer = memnew(Timer);
|
||||
symbol_tooltip_timer->set_wait_time(0.5); // See `_apply_project_settings()`.
|
||||
symbol_tooltip_timer->set_one_shot(true);
|
||||
symbol_tooltip_timer->connect("timeout", callable_mp(this, &CodeEdit::_on_symbol_tooltip_timer_timeout));
|
||||
add_child(symbol_tooltip_timer, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
connect("lines_edited_from", callable_mp(this, &CodeEdit::_lines_edited_from));
|
||||
connect("text_set", callable_mp(this, &CodeEdit::_text_set));
|
||||
connect(SceneStringName(text_changed), callable_mp(this, &CodeEdit::_text_changed));
|
||||
|
|
@ -3698,6 +3819,7 @@ CodeEdit::CodeEdit() {
|
|||
}
|
||||
|
||||
CodeEdit::~CodeEdit() {
|
||||
_clear_line_number_text_cache();
|
||||
}
|
||||
|
||||
// Return true if l should come before r
|
||||
|
|
|
|||
|
|
@ -113,6 +113,9 @@ private:
|
|||
int line_number_gutter = -1;
|
||||
int line_number_digits = 1;
|
||||
String line_number_padding = " ";
|
||||
HashMap<int, RID> line_number_text_cache;
|
||||
void _clear_line_number_text_cache();
|
||||
void _update_line_number_gutter_width();
|
||||
void _line_number_draw_callback(int p_line, int p_gutter, const Rect2 &p_region);
|
||||
|
||||
/* Fold Gutter */
|
||||
|
|
@ -230,10 +233,16 @@ private:
|
|||
|
||||
/* Symbol lookup */
|
||||
bool symbol_lookup_on_click_enabled = false;
|
||||
Point2i symbol_lookup_pos; // Column and line.
|
||||
String symbol_lookup_new_word;
|
||||
String symbol_lookup_word;
|
||||
|
||||
String symbol_lookup_new_word = "";
|
||||
String symbol_lookup_word = "";
|
||||
Point2i symbol_lookup_pos;
|
||||
/* Symbol tooltip */
|
||||
bool symbol_tooltip_on_hover_enabled = false;
|
||||
Point2i symbol_tooltip_pos; // Column and line.
|
||||
String symbol_tooltip_word;
|
||||
Timer *symbol_tooltip_timer = nullptr;
|
||||
void _on_symbol_tooltip_timer_timeout();
|
||||
|
||||
/* Visual */
|
||||
struct ThemeCache {
|
||||
|
|
@ -245,15 +254,16 @@ private:
|
|||
Ref<Texture2D> can_fold_code_region_icon;
|
||||
Ref<Texture2D> folded_code_region_icon;
|
||||
Ref<Texture2D> folded_eol_icon;
|
||||
Ref<Texture2D> completion_color_bg;
|
||||
|
||||
Color breakpoint_color = Color(1, 1, 1);
|
||||
Ref<Texture2D> breakpoint_icon = Ref<Texture2D>();
|
||||
Ref<Texture2D> breakpoint_icon;
|
||||
|
||||
Color bookmark_color = Color(1, 1, 1);
|
||||
Ref<Texture2D> bookmark_icon = Ref<Texture2D>();
|
||||
Ref<Texture2D> bookmark_icon;
|
||||
|
||||
Color executing_line_color = Color(1, 1, 1);
|
||||
Ref<Texture2D> executing_line_icon = Ref<Texture2D>();
|
||||
Ref<Texture2D> executing_line_icon;
|
||||
|
||||
Color line_number_color = Color(1, 1, 1);
|
||||
|
||||
|
|
@ -300,6 +310,8 @@ private:
|
|||
void _text_set();
|
||||
void _text_changed();
|
||||
|
||||
void _apply_project_settings();
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
|
@ -493,6 +505,10 @@ public:
|
|||
|
||||
void set_symbol_lookup_word_as_valid(bool p_valid);
|
||||
|
||||
/* Symbol tooltip */
|
||||
void set_symbol_tooltip_on_hover_enabled(bool p_enabled);
|
||||
bool is_symbol_tooltip_on_hover_enabled() const;
|
||||
|
||||
/* Text manipulation */
|
||||
void move_lines_up();
|
||||
void move_lines_down();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
#include "core/math/color.h"
|
||||
#include "scene/gui/slider.h"
|
||||
#include "thirdparty/misc/ok_color.h"
|
||||
#include "scene/resources/gradient_texture.h"
|
||||
|
||||
ColorMode::ColorMode(ColorPicker *p_color_picker) {
|
||||
color_picker = p_color_picker;
|
||||
|
|
@ -267,7 +267,7 @@ void ColorModeRAW::slider_draw(int p_which) {
|
|||
}
|
||||
|
||||
bool ColorModeRAW::apply_theme() const {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int i = 0; i < ColorPicker::SLIDER_COUNT; i++) {
|
||||
HSlider *slider = color_picker->get_slider(i);
|
||||
slider->remove_theme_icon_override("grabber");
|
||||
slider->remove_theme_icon_override("grabber_highlight");
|
||||
|
|
@ -399,8 +399,29 @@ void ColorModeOKHSL::slider_draw(int p_which) {
|
|||
slider->draw_polygon(pos, col);
|
||||
|
||||
if (p_which == 0) { // H
|
||||
Ref<Texture2D> hue = color_picker->theme_cache.color_okhsl_hue;
|
||||
slider->draw_texture_rect(hue, Rect2(Vector2(), Vector2(size.x, margin)), false, Color::from_hsv(0, 0, color.get_ok_hsl_l() * 2.0, color.get_ok_hsl_s()));
|
||||
return;
|
||||
const int precision = 7;
|
||||
|
||||
Ref<Gradient> hue_gradient;
|
||||
hue_gradient.instantiate();
|
||||
PackedFloat32Array offsets;
|
||||
offsets.resize(precision);
|
||||
PackedColorArray colors;
|
||||
colors.resize(precision);
|
||||
|
||||
for (int i = 0; i < precision; i++) {
|
||||
float h = i / float(precision - 1);
|
||||
offsets.write[i] = h;
|
||||
colors.write[i] = Color::from_ok_hsl(h, color.get_ok_hsl_s(), color.get_ok_hsl_l());
|
||||
}
|
||||
hue_gradient->set_offsets(offsets);
|
||||
hue_gradient->set_colors(colors);
|
||||
hue_gradient->set_interpolation_color_space(Gradient::ColorSpace::GRADIENT_COLOR_SPACE_OKLAB);
|
||||
if (hue_texture.is_null()) {
|
||||
hue_texture.instantiate();
|
||||
hue_texture->set_width(800);
|
||||
hue_texture->set_height(6);
|
||||
}
|
||||
hue_texture->set_gradient(hue_gradient);
|
||||
slider->draw_texture_rect(hue_texture, Rect2(Vector2(), Vector2(size.x, margin)), false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#include "scene/gui/color_picker.h"
|
||||
|
||||
struct Color;
|
||||
class GradientTexture2D;
|
||||
|
||||
class ColorMode {
|
||||
public:
|
||||
|
|
@ -41,23 +41,23 @@ public:
|
|||
|
||||
virtual String get_name() const = 0;
|
||||
|
||||
virtual int get_slider_count() const { return 3; };
|
||||
virtual int get_slider_count() const { return 3; }
|
||||
virtual float get_slider_step() const = 0;
|
||||
virtual float get_spinbox_arrow_step() const { return get_slider_step(); };
|
||||
virtual float get_spinbox_arrow_step() const { return get_slider_step(); }
|
||||
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 _value_changed(){};
|
||||
virtual void _value_changed() {}
|
||||
|
||||
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(){};
|
||||
virtual ~ColorMode() {}
|
||||
};
|
||||
|
||||
class ColorModeHSV : public ColorMode {
|
||||
|
|
@ -81,7 +81,7 @@ public:
|
|||
virtual void slider_draw(int p_which) override;
|
||||
|
||||
ColorModeHSV(ColorPicker *p_color_picker) :
|
||||
ColorMode(p_color_picker){};
|
||||
ColorMode(p_color_picker) {}
|
||||
};
|
||||
|
||||
class ColorModeRGB : public ColorMode {
|
||||
|
|
@ -100,7 +100,7 @@ public:
|
|||
virtual void slider_draw(int p_which) override;
|
||||
|
||||
ColorModeRGB(ColorPicker *p_color_picker) :
|
||||
ColorMode(p_color_picker){};
|
||||
ColorMode(p_color_picker) {}
|
||||
};
|
||||
|
||||
class ColorModeRAW : public ColorMode {
|
||||
|
|
@ -122,7 +122,7 @@ public:
|
|||
virtual bool apply_theme() const override;
|
||||
|
||||
ColorModeRAW(ColorPicker *p_color_picker) :
|
||||
ColorMode(p_color_picker){};
|
||||
ColorMode(p_color_picker) {}
|
||||
};
|
||||
|
||||
class ColorModeOKHSL : public ColorMode {
|
||||
|
|
@ -131,6 +131,7 @@ public:
|
|||
float slider_max[4] = { 359, 100, 100, 255 };
|
||||
float cached_hue = 0.0;
|
||||
float cached_saturation = 0.0;
|
||||
Ref<GradientTexture2D> hue_texture = nullptr;
|
||||
|
||||
virtual String get_name() const override { return "OKHSL"; }
|
||||
|
||||
|
|
@ -147,9 +148,9 @@ public:
|
|||
virtual ColorPicker::PickerShapeType get_shape_override() const override { return ColorPicker::SHAPE_OKHSL_CIRCLE; }
|
||||
|
||||
ColorModeOKHSL(ColorPicker *p_color_picker) :
|
||||
ColorMode(p_color_picker){};
|
||||
ColorMode(p_color_picker) {}
|
||||
|
||||
~ColorModeOKHSL(){};
|
||||
~ColorModeOKHSL() {}
|
||||
};
|
||||
|
||||
#endif // COLOR_MODE_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -31,28 +31,25 @@
|
|||
#ifndef COLOR_PICKER_H
|
||||
#define COLOR_PICKER_H
|
||||
|
||||
#include "scene/gui/aspect_ratio_container.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/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/menu_button.h"
|
||||
#include "scene/gui/option_button.h"
|
||||
#include "scene/gui/panel.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"
|
||||
#include "scene/resources/style_box_flat.h"
|
||||
|
||||
class AspectRatioContainer;
|
||||
class ColorMode;
|
||||
class ColorModeRGB;
|
||||
class ColorModeHSV;
|
||||
class ColorModeRAW;
|
||||
class ColorModeOKHSL;
|
||||
class ColorPickerShape;
|
||||
class FileDialog;
|
||||
class GridContainer;
|
||||
class HSlider;
|
||||
class Label;
|
||||
class LineEdit;
|
||||
class MarginContainer;
|
||||
class MenuButton;
|
||||
class OptionButton;
|
||||
class PopupMenu;
|
||||
class SpinBox;
|
||||
class StyleBoxFlat;
|
||||
class TextureRect;
|
||||
|
||||
class ColorPresetButton : public BaseButton {
|
||||
GDCLASS(ColorPresetButton, BaseButton);
|
||||
|
|
@ -107,14 +104,22 @@ public:
|
|||
SHAPE_MAX
|
||||
};
|
||||
|
||||
static const int SLIDER_COUNT = 4;
|
||||
static const int SLIDER_COUNT = 3;
|
||||
|
||||
private:
|
||||
static Ref<Shader> wheel_shader;
|
||||
static Ref<Shader> circle_shader;
|
||||
static Ref<Shader> circle_ok_color_shader;
|
||||
static List<Color> preset_cache;
|
||||
static List<Color> recent_preset_cache;
|
||||
enum class MenuOption {
|
||||
MENU_SAVE,
|
||||
MENU_SAVE_AS,
|
||||
MENU_LOAD,
|
||||
MENU_QUICKLOAD,
|
||||
MENU_CLEAR,
|
||||
};
|
||||
|
||||
static inline Ref<Shader> wheel_shader;
|
||||
static inline Ref<Shader> circle_shader;
|
||||
static inline Ref<Shader> circle_ok_color_shader;
|
||||
static inline List<Color> preset_cache;
|
||||
static inline List<Color> recent_preset_cache;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Object *editor_settings = nullptr;
|
||||
|
|
@ -122,18 +127,25 @@ private:
|
|||
|
||||
int current_slider_count = SLIDER_COUNT;
|
||||
static const int MODE_BUTTON_COUNT = 3;
|
||||
const float WHEEL_RADIUS = 0.42;
|
||||
|
||||
bool slider_theme_modified = true;
|
||||
|
||||
Vector<ColorMode *> modes;
|
||||
|
||||
Popup *picker_window = nullptr;
|
||||
TextureRect *picker_texture_zoom = nullptr;
|
||||
Panel *picker_preview = nullptr;
|
||||
Panel *picker_preview_color = nullptr;
|
||||
Ref<StyleBoxFlat> picker_preview_style_box;
|
||||
Ref<StyleBoxFlat> picker_preview_style_box_color;
|
||||
|
||||
// Legacy color picking.
|
||||
TextureRect *picker_texture_rect = nullptr;
|
||||
Panel *picker_preview = nullptr;
|
||||
Label *picker_preview_label = nullptr;
|
||||
Ref<StyleBoxFlat> picker_preview_style_box;
|
||||
Color picker_color;
|
||||
FileDialog *file_dialog = nullptr;
|
||||
MenuButton *menu_btn = nullptr;
|
||||
PopupMenu *options_menu = nullptr;
|
||||
|
||||
MarginContainer *internal_margin = nullptr;
|
||||
Control *uv_edit = nullptr;
|
||||
|
|
@ -145,10 +157,13 @@ private:
|
|||
Control *wheel = nullptr;
|
||||
Control *wheel_uv = nullptr;
|
||||
TextureRect *sample = nullptr;
|
||||
VBoxContainer *swatches_vbc = nullptr;
|
||||
GridContainer *preset_container = nullptr;
|
||||
HBoxContainer *recent_preset_hbc = nullptr;
|
||||
Button *btn_add_preset = nullptr;
|
||||
Button *btn_pick = nullptr;
|
||||
Label *palette_name = nullptr;
|
||||
String palette_path;
|
||||
Button *btn_preset = nullptr;
|
||||
Button *btn_recent_preset = nullptr;
|
||||
PopupMenu *shape_popup = nullptr;
|
||||
|
|
@ -164,6 +179,10 @@ private:
|
|||
ColorPresetButton *selected_recent_preset = nullptr;
|
||||
Ref<ButtonGroup> preset_group;
|
||||
Ref<ButtonGroup> recent_preset_group;
|
||||
#ifdef TOOLS_ENABLED
|
||||
Callable quick_open_callback;
|
||||
Callable palette_saved_callback;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
OptionButton *mode_option_button = nullptr;
|
||||
|
||||
|
|
@ -192,6 +211,7 @@ private:
|
|||
|
||||
Color color;
|
||||
Color old_color;
|
||||
Color pre_picking_color;
|
||||
bool is_picking_color = false;
|
||||
|
||||
bool display_old_color = false;
|
||||
|
|
@ -212,6 +232,11 @@ private:
|
|||
float h = 0.0;
|
||||
float s = 0.0;
|
||||
float v = 0.0;
|
||||
|
||||
float ok_hsl_h = 0.0;
|
||||
float ok_hsl_s = 0.0;
|
||||
float ok_hsl_l = 0.0;
|
||||
|
||||
Color last_color;
|
||||
|
||||
struct ThemeCache {
|
||||
|
|
@ -226,6 +251,7 @@ private:
|
|||
|
||||
bool center_slider_grabbers = true;
|
||||
|
||||
Ref<Texture2D> menu_option;
|
||||
Ref<Texture2D> screen_picker;
|
||||
Ref<Texture2D> expanded_arrow;
|
||||
Ref<Texture2D> folded_arrow;
|
||||
|
|
@ -240,8 +266,8 @@ private:
|
|||
Ref<Texture2D> sample_revert;
|
||||
Ref<Texture2D> overbright_indicator;
|
||||
Ref<Texture2D> picker_cursor;
|
||||
Ref<Texture2D> picker_cursor_bg;
|
||||
Ref<Texture2D> color_hue;
|
||||
Ref<Texture2D> color_okhsl_hue;
|
||||
|
||||
/* Mode buttons */
|
||||
Ref<StyleBox> mode_button_normal;
|
||||
|
|
@ -278,7 +304,11 @@ private:
|
|||
void _add_preset_pressed();
|
||||
void _html_focus_exit();
|
||||
void _pick_button_pressed();
|
||||
void _target_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _pick_finished();
|
||||
void _update_menu_items();
|
||||
void _options_menu_cbk(int p_which);
|
||||
|
||||
// Legacy color picking.
|
||||
void _pick_button_pressed_legacy();
|
||||
void _picker_texture_input(const Ref<InputEvent> &p_event);
|
||||
|
|
@ -286,6 +316,8 @@ private:
|
|||
inline int _get_preset_size();
|
||||
void _add_preset_button(int p_size, const Color &p_color);
|
||||
void _add_recent_preset_button(int p_size, const Color &p_color);
|
||||
void _save_palette(bool p_is_save_as);
|
||||
void _load_palette();
|
||||
|
||||
void _show_hide_preset(const bool &p_is_btn_pressed, Button *p_btn_preset, Container *p_preset_container);
|
||||
void _update_drop_down_arrow(const bool &p_is_btn_pressed, Button *p_btn_preset);
|
||||
|
|
@ -305,6 +337,8 @@ protected:
|
|||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
void set_editor_settings(Object *p_editor_settings);
|
||||
void set_quick_open_callback(const Callable &p_file_selected);
|
||||
void set_palette_saved_callback(const Callable &p_palette_saved);
|
||||
#endif
|
||||
HSlider *get_slider(int idx);
|
||||
Vector<float> get_active_slider_values();
|
||||
|
|
@ -322,6 +356,8 @@ public:
|
|||
Color get_pick_color() const;
|
||||
void set_old_color(const Color &p_color);
|
||||
Color get_old_color() const;
|
||||
void _quick_open_palette_file_selected(const String &p_path);
|
||||
void _palette_file_selected(const String &p_path);
|
||||
|
||||
void set_display_old_color(bool p_enabled);
|
||||
bool is_displaying_old_color() const;
|
||||
|
|
|
|||
|
|
@ -140,15 +140,15 @@ void Container::queue_sort() {
|
|||
pending_sort = true;
|
||||
}
|
||||
|
||||
Control *Container::as_sortable_control(Node *p_node, SortableVisbilityMode p_visibility_mode) const {
|
||||
Control *Container::as_sortable_control(Node *p_node, SortableVisibilityMode p_visibility_mode) const {
|
||||
Control *c = Object::cast_to<Control>(p_node);
|
||||
if (!c || c->is_set_as_top_level()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (p_visibility_mode == SortableVisbilityMode::VISIBLE && !c->is_visible()) {
|
||||
if (p_visibility_mode == SortableVisibilityMode::VISIBLE && !c->is_visible()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (p_visibility_mode == SortableVisbilityMode::VISIBLE_IN_TREE && !c->is_visible_in_tree()) {
|
||||
if (p_visibility_mode == SortableVisibilityMode::VISIBLE_IN_TREE && !c->is_visible_in_tree()) {
|
||||
return nullptr;
|
||||
}
|
||||
return c;
|
||||
|
|
|
|||
|
|
@ -41,14 +41,14 @@ class Container : public Control {
|
|||
void _child_minsize_changed();
|
||||
|
||||
protected:
|
||||
enum class SortableVisbilityMode {
|
||||
enum class SortableVisibilityMode {
|
||||
VISIBLE,
|
||||
VISIBLE_IN_TREE,
|
||||
IGNORE,
|
||||
};
|
||||
|
||||
void queue_sort();
|
||||
Control *as_sortable_control(Node *p_node, SortableVisbilityMode p_visibility_mode = SortableVisbilityMode::VISIBLE_IN_TREE) const;
|
||||
Control *as_sortable_control(Node *p_node, SortableVisibilityMode p_visibility_mode = SortableVisibilityMode::VISIBLE_IN_TREE) const;
|
||||
|
||||
virtual void add_child_notify(Node *p_child) override;
|
||||
virtual void move_child_notify(Node *p_child) override;
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
/**************************************************************************/
|
||||
/* control.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 DISABLE_DEPRECATED
|
||||
|
||||
void Control::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_theme_icon", "name", "theme_type"), &Control::get_theme_icon, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_theme_stylebox", "name", "theme_type"), &Control::get_theme_stylebox, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_theme_font", "name", "theme_type"), &Control::get_theme_font, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_theme_font_size", "name", "theme_type"), &Control::get_theme_font_size, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_theme_color", "name", "theme_type"), &Control::get_theme_color, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_theme_constant", "name", "theme_type"), &Control::get_theme_constant, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("has_theme_icon", "name", "theme_type"), &Control::has_theme_icon, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("has_theme_stylebox", "name", "theme_type"), &Control::has_theme_stylebox, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("has_theme_font", "name", "theme_type"), &Control::has_theme_font, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("has_theme_font_size", "name", "theme_type"), &Control::has_theme_font_size, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("has_theme_color", "name", "theme_type"), &Control::has_theme_color, DEFVAL(""));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("has_theme_constant", "name", "theme_type"), &Control::has_theme_constant, DEFVAL(""));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -29,17 +29,12 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#include "control.h"
|
||||
#include "control.compat.inc"
|
||||
|
||||
#include "container.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/panel.h"
|
||||
#include "core/string/translation_server.h"
|
||||
#include "scene/gui/scroll_container.h"
|
||||
#include "scene/main/canvas_layer.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
|
@ -49,7 +44,7 @@
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/plugins/control_editor_plugin.h"
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
// Editor plugin interoperability.
|
||||
|
||||
|
|
@ -125,11 +120,11 @@ void Control::_edit_set_state(const Dictionary &p_state) {
|
|||
void Control::_edit_set_position(const Point2 &p_position) {
|
||||
ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
|
||||
set_position(p_position, ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled() && get_parent_control());
|
||||
};
|
||||
}
|
||||
|
||||
Point2 Control::_edit_get_position() const {
|
||||
return get_position();
|
||||
};
|
||||
}
|
||||
|
||||
void Control::_edit_set_scale(const Size2 &p_scale) {
|
||||
set_scale(p_scale);
|
||||
|
|
@ -145,14 +140,6 @@ void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
|
|||
set_size(p_edit_rect.size.snappedf(1), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled());
|
||||
}
|
||||
|
||||
Rect2 Control::_edit_get_rect() const {
|
||||
return Rect2(Point2(), get_size());
|
||||
}
|
||||
|
||||
bool Control::_edit_use_rect() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Control::_edit_set_rotation(real_t p_rotation) {
|
||||
set_rotation(p_rotation);
|
||||
}
|
||||
|
|
@ -183,7 +170,17 @@ bool Control::_edit_use_pivot() const {
|
|||
Size2 Control::_edit_get_minimum_size() const {
|
||||
return get_combined_minimum_size();
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
Rect2 Control::_edit_get_rect() const {
|
||||
return Rect2(Point2(), get_size());
|
||||
}
|
||||
|
||||
bool Control::_edit_use_rect() const {
|
||||
return true;
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void Control::reparent(Node *p_parent, bool p_keep_global_transform) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
|
|
@ -244,11 +241,11 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
|
|||
}
|
||||
CanvasItem::get_argument_options(p_function, p_idx, r_options);
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
PackedStringArray Control::get_configuration_warnings() const {
|
||||
ERR_READ_THREAD_GUARD_V(PackedStringArray());
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = CanvasItem::get_configuration_warnings();
|
||||
|
||||
if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) {
|
||||
warnings.push_back(RTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
|
||||
|
|
@ -668,7 +665,7 @@ Rect2 Control::get_parent_anchorable_rect() const {
|
|||
|
||||
#else
|
||||
parent_rect = get_viewport()->get_visible_rect();
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
}
|
||||
|
||||
return parent_rect;
|
||||
|
|
@ -682,12 +679,10 @@ Size2 Control::get_parent_area_size() const {
|
|||
// Positioning and sizing.
|
||||
|
||||
Transform2D Control::_get_internal_transform() const {
|
||||
Transform2D rot_scale;
|
||||
rot_scale.set_rotation_and_scale(data.rotation, data.scale);
|
||||
Transform2D offset;
|
||||
offset.set_origin(-data.pivot_offset);
|
||||
|
||||
return offset.affine_inverse() * (rot_scale * offset);
|
||||
// T(pivot_offset) * R(rotation) * S(scale) * T(-pivot_offset)
|
||||
Transform2D xform(data.rotation, data.scale, 0.0f, data.pivot_offset);
|
||||
xform.translate_local(-data.pivot_offset);
|
||||
return xform;
|
||||
}
|
||||
|
||||
void Control::_update_canvas_item_transform() {
|
||||
|
|
@ -1403,7 +1398,7 @@ void Control::set_position(const Point2 &p_point, bool p_keep_offsets) {
|
|||
data.pos_cache = p_point;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
if (p_keep_offsets) {
|
||||
_compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
|
||||
|
|
@ -1447,7 +1442,7 @@ void Control::_set_size(const Size2 &p_size) {
|
|||
if (data.size_warning && (data.anchor[SIDE_LEFT] != data.anchor[SIDE_RIGHT] || data.anchor[SIDE_TOP] != data.anchor[SIDE_BOTTOM])) {
|
||||
WARN_PRINT("Nodes with non-equal opposite anchors will have their size overridden after _ready(). \nIf you want to set size, change the anchors or consider using set_deferred().");
|
||||
}
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
set_size(p_size);
|
||||
}
|
||||
|
||||
|
|
@ -1469,7 +1464,7 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) {
|
|||
data.size_cache = new_size;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
if (p_keep_offsets) {
|
||||
_compute_anchors(Rect2(data.pos_cache, new_size), data.offset, data.anchor);
|
||||
|
|
@ -1677,7 +1672,7 @@ Size2 Control::get_custom_minimum_size() const {
|
|||
return data.custom_minimum_size;
|
||||
}
|
||||
|
||||
void Control::_update_minimum_size_cache() {
|
||||
void Control::_update_minimum_size_cache() const {
|
||||
Size2 minsize = get_minimum_size();
|
||||
minsize = minsize.max(data.custom_minimum_size);
|
||||
|
||||
|
|
@ -1688,7 +1683,7 @@ void Control::_update_minimum_size_cache() {
|
|||
Size2 Control::get_combined_minimum_size() const {
|
||||
ERR_READ_THREAD_GUARD_V(Size2());
|
||||
if (!data.minimum_size_valid) {
|
||||
const_cast<Control *>(this)->_update_minimum_size_cache();
|
||||
_update_minimum_size_cache();
|
||||
}
|
||||
return data.minimum_size_cache;
|
||||
}
|
||||
|
|
@ -1748,10 +1743,10 @@ void Control::_size_changed() {
|
|||
// so an up to date global transform could be obtained when handling these.
|
||||
_notify_transform();
|
||||
|
||||
item_rect_changed(size_changed);
|
||||
if (size_changed) {
|
||||
notification(NOTIFICATION_RESIZED);
|
||||
}
|
||||
item_rect_changed(size_changed);
|
||||
}
|
||||
|
||||
if (pos_changed && !size_changed) {
|
||||
|
|
@ -2286,18 +2281,9 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
|
|||
return c;
|
||||
}
|
||||
|
||||
real_t dist = 1e7;
|
||||
real_t square_of_dist = 1e14;
|
||||
Control *result = nullptr;
|
||||
|
||||
Point2 points[4];
|
||||
|
||||
Transform2D xform = get_global_transform();
|
||||
|
||||
points[0] = xform.xform(Point2());
|
||||
points[1] = xform.xform(Point2(get_size().x, 0));
|
||||
points[2] = xform.xform(get_size());
|
||||
points[3] = xform.xform(Point2(0, get_size().y));
|
||||
|
||||
const Vector2 dir[4] = {
|
||||
Vector2(-1, 0),
|
||||
Vector2(0, -1),
|
||||
|
|
@ -2307,18 +2293,73 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
|
|||
|
||||
Vector2 vdir = dir[p_side];
|
||||
|
||||
real_t maxd = -1e7;
|
||||
Rect2 r = get_global_rect();
|
||||
real_t begin_d = vdir.dot(r.get_position());
|
||||
real_t end_d = vdir.dot(r.get_end());
|
||||
real_t maxd = MAX(begin_d, end_d);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
real_t d = vdir.dot(points[i]);
|
||||
if (d > maxd) {
|
||||
maxd = d;
|
||||
}
|
||||
}
|
||||
Rect2 clamp = Rect2(-1e7, -1e7, 2e7, 2e7);
|
||||
Rect2 result_rect;
|
||||
|
||||
Node *base = this;
|
||||
|
||||
while (base) {
|
||||
ScrollContainer *sc = Object::cast_to<ScrollContainer>(base);
|
||||
|
||||
if (sc) {
|
||||
Rect2 sc_r = sc->get_global_rect();
|
||||
bool follow_focus = sc->is_following_focus();
|
||||
|
||||
if (result && !follow_focus && !sc_r.intersects(result_rect)) {
|
||||
result = nullptr; // Skip invisible control.
|
||||
}
|
||||
|
||||
if (result == nullptr) {
|
||||
real_t sc_begin_d = vdir.dot(sc_r.get_position());
|
||||
real_t sc_end_d = vdir.dot(sc_r.get_end());
|
||||
real_t sc_maxd = sc_begin_d;
|
||||
real_t sc_mind = sc_end_d;
|
||||
if (sc_begin_d < sc_end_d) {
|
||||
sc_maxd = sc_end_d;
|
||||
sc_mind = sc_begin_d;
|
||||
}
|
||||
|
||||
if (!follow_focus && maxd < sc_mind) {
|
||||
// Reposition to find visible control.
|
||||
maxd = sc_mind;
|
||||
r.set_position(r.get_position() + (sc_mind - maxd) * vdir);
|
||||
}
|
||||
|
||||
if (follow_focus || sc_maxd > maxd) {
|
||||
_window_find_focus_neighbor(vdir, base, r, clamp, maxd, square_of_dist, &result);
|
||||
}
|
||||
|
||||
if (result == nullptr) {
|
||||
// Reposition to search upwards.
|
||||
maxd = sc_maxd;
|
||||
r.set_position(r.get_position() + (sc_maxd - maxd) * vdir);
|
||||
} else {
|
||||
result_rect = result->get_global_rect();
|
||||
if (follow_focus) {
|
||||
real_t r_begin_d = vdir.dot(result_rect.get_position());
|
||||
real_t r_end_d = vdir.dot(result_rect.get_end());
|
||||
real_t r_maxd = r_begin_d;
|
||||
real_t r_mind = r_end_d;
|
||||
if (r_begin_d < r_end_d) {
|
||||
r_maxd = r_end_d;
|
||||
r_mind = r_begin_d;
|
||||
}
|
||||
|
||||
if (r_maxd > sc_maxd) {
|
||||
result_rect.set_position(result_rect.get_position() + (sc_maxd - r_maxd) * vdir);
|
||||
} else if (r_mind < sc_mind) {
|
||||
result_rect.set_position(result_rect.get_position() + (sc_mind - r_mind) * vdir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Control *c = Object::cast_to<Control>(base);
|
||||
if (c) {
|
||||
if (c->data.RI) {
|
||||
|
|
@ -2328,11 +2369,15 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
|
|||
base = base->get_parent();
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!base) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_window_find_focus_neighbor(vdir, base, points, maxd, dist, &result);
|
||||
_window_find_focus_neighbor(vdir, base, r, clamp, maxd, square_of_dist, &result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -2341,49 +2386,79 @@ Control *Control::find_valid_focus_neighbor(Side p_side) const {
|
|||
return const_cast<Control *>(this)->_get_focus_neighbor(p_side);
|
||||
}
|
||||
|
||||
void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest) {
|
||||
void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Rect2 &p_rect, const Rect2 &p_clamp, real_t p_min, real_t &r_closest_dist_squared, Control **r_closest) {
|
||||
if (Object::cast_to<Viewport>(p_at)) {
|
||||
return; //bye
|
||||
return; // Bye.
|
||||
}
|
||||
|
||||
Control *c = Object::cast_to<Control>(p_at);
|
||||
Container *container = Object::cast_to<Container>(p_at);
|
||||
bool in_container = container ? container->is_ancestor_of(this) : false;
|
||||
|
||||
if (c && c != this && c->get_focus_mode() == FOCUS_ALL && c->is_visible_in_tree()) {
|
||||
Point2 points[4];
|
||||
if (c && c != this && c->get_focus_mode() == FOCUS_ALL && !in_container && p_clamp.intersects(c->get_global_rect())) {
|
||||
Rect2 r_c = c->get_global_rect();
|
||||
r_c = r_c.intersection(p_clamp);
|
||||
real_t begin_d = p_dir.dot(r_c.get_position());
|
||||
real_t end_d = p_dir.dot(r_c.get_end());
|
||||
real_t max = MAX(begin_d, end_d);
|
||||
|
||||
Transform2D xform = c->get_global_transform();
|
||||
// Use max to allow navigation to overlapping controls (for ScrollContainer case).
|
||||
if (max > (p_min + CMP_EPSILON)) {
|
||||
// Calculate the shortest distance. (No shear transform)
|
||||
// Flip along axis(es) so that C falls in the first quadrant of c (as origin) for easy calculation.
|
||||
// The same transformation would put the direction vector in the positive direction (+x or +y).
|
||||
// | -------------
|
||||
// | | | |
|
||||
// | |-----C-----|
|
||||
// ----|---a | | |
|
||||
// | | | b------------
|
||||
// -|---c---|----------------------->
|
||||
// | | |
|
||||
// ----|----
|
||||
// cC = ca + ab + bC
|
||||
// The shortest distance is the vector ab's length or its positive projection length.
|
||||
|
||||
points[0] = xform.xform(Point2());
|
||||
points[1] = xform.xform(Point2(c->get_size().x, 0));
|
||||
points[2] = xform.xform(c->get_size());
|
||||
points[3] = xform.xform(Point2(0, c->get_size().y));
|
||||
Vector2 cC_origin = r_c.get_center() - p_rect.get_center();
|
||||
Vector2 cC = cC_origin.abs(); // Converted to fall in the first quadrant of c.
|
||||
|
||||
real_t min = 1e7;
|
||||
Vector2 ab = cC - 0.5 * r_c.get_size() - 0.5 * p_rect.get_size();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
real_t d = p_dir.dot(points[i]);
|
||||
if (d < min) {
|
||||
min = d;
|
||||
real_t min_d_squared = 0.0;
|
||||
if (ab.x > 0.0) {
|
||||
min_d_squared += ab.x * ab.x;
|
||||
}
|
||||
if (ab.y > 0.0) {
|
||||
min_d_squared += ab.y * ab.y;
|
||||
}
|
||||
|
||||
if (min_d_squared < r_closest_dist_squared || *r_closest == nullptr) {
|
||||
r_closest_dist_squared = min_d_squared;
|
||||
*r_closest = c;
|
||||
} else if (min_d_squared == r_closest_dist_squared) {
|
||||
// Tie-breaking aims to address situations where a potential focus neighbor's bounding rect
|
||||
// is right next to the currently focused control (e.g. in BoxContainer with
|
||||
// separation overridden to 0). This needs specific handling so that the correct
|
||||
// focus neighbor is selected.
|
||||
|
||||
Point2 p_center = p_rect.get_center();
|
||||
Control *closest = *r_closest;
|
||||
Point2 closest_center = closest->get_global_rect().get_center();
|
||||
|
||||
// Tie-break in favor of the control most aligned with p_dir.
|
||||
if (Math::abs(p_dir.cross(cC_origin)) < Math::abs(p_dir.cross(closest_center - p_center))) {
|
||||
*r_closest = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (min > (p_min - CMP_EPSILON)) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Vector2 la = p_points[i];
|
||||
Vector2 lb = p_points[(i + 1) % 4];
|
||||
|
||||
for (int j = 0; j < 4; j++) {
|
||||
Vector2 fa = points[j];
|
||||
Vector2 fb = points[(j + 1) % 4];
|
||||
|
||||
Vector2 pa, pb;
|
||||
real_t d = Geometry2D::get_closest_points_between_segments(la, lb, fa, fb, pa, pb);
|
||||
//real_t d = Geometry2D::get_closest_distance_between_segments(Vector3(la.x,la.y,0),Vector3(lb.x,lb.y,0),Vector3(fa.x,fa.y,0),Vector3(fb.x,fb.y,0));
|
||||
if (d < r_closest_dist) {
|
||||
r_closest_dist = d;
|
||||
*r_closest = c;
|
||||
}
|
||||
}
|
||||
ScrollContainer *sc = Object::cast_to<ScrollContainer>(c);
|
||||
Rect2 intersection = p_clamp;
|
||||
if (sc) {
|
||||
if (!sc->is_following_focus() || !in_container) {
|
||||
intersection = p_clamp.intersection(sc->get_global_rect());
|
||||
if (!intersection.has_area()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2391,10 +2466,18 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons
|
|||
for (int i = 0; i < p_at->get_child_count(); i++) {
|
||||
Node *child = p_at->get_child(i);
|
||||
Control *childc = Object::cast_to<Control>(child);
|
||||
if (childc && childc->data.RI) {
|
||||
continue; //subwindow, ignore
|
||||
if (childc) {
|
||||
if (childc->data.RI) {
|
||||
continue; // Subwindow, ignore.
|
||||
}
|
||||
if (!childc->is_visible_in_tree()) {
|
||||
continue; // Skip invisible node trees.
|
||||
}
|
||||
if (Object::cast_to<ScrollContainer>(childc) && childc->is_ancestor_of(this)) {
|
||||
continue; // Already searched in it, skip it.
|
||||
}
|
||||
}
|
||||
_window_find_focus_neighbor(p_dir, p_at->get_child(i), p_points, p_min, r_closest_dist, r_closest);
|
||||
_window_find_focus_neighbor(p_dir, child, p_rect, intersection, p_min, r_closest_dist_squared, r_closest);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2577,8 +2660,8 @@ Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringNam
|
|||
return data.theme_icon_cache[p_theme_type][p_name];
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
Ref<Texture2D> icon = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types);
|
||||
data.theme_icon_cache[p_theme_type][p_name] = icon;
|
||||
return icon;
|
||||
|
|
@ -2601,8 +2684,8 @@ Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const String
|
|||
return data.theme_style_cache[p_theme_type][p_name];
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
Ref<StyleBox> style = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
|
||||
data.theme_style_cache[p_theme_type][p_name] = style;
|
||||
return style;
|
||||
|
|
@ -2625,8 +2708,8 @@ Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_
|
|||
return data.theme_font_cache[p_theme_type][p_name];
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
Ref<Font> font = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types);
|
||||
data.theme_font_cache[p_theme_type][p_name] = font;
|
||||
return font;
|
||||
|
|
@ -2649,8 +2732,8 @@ int Control::get_theme_font_size(const StringName &p_name, const StringName &p_t
|
|||
return data.theme_font_size_cache[p_theme_type][p_name];
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
int font_size = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
|
||||
data.theme_font_size_cache[p_theme_type][p_name] = font_size;
|
||||
return font_size;
|
||||
|
|
@ -2673,8 +2756,8 @@ Color Control::get_theme_color(const StringName &p_name, const StringName &p_the
|
|||
return data.theme_color_cache[p_theme_type][p_name];
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
Color color = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types);
|
||||
data.theme_color_cache[p_theme_type][p_name] = color;
|
||||
return color;
|
||||
|
|
@ -2697,8 +2780,8 @@ int Control::get_theme_constant(const StringName &p_name, const StringName &p_th
|
|||
return data.theme_constant_cache[p_theme_type][p_name];
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
int constant = data.theme_owner->get_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
|
||||
data.theme_constant_cache[p_theme_type][p_name] = constant;
|
||||
return constant;
|
||||
|
|
@ -2729,7 +2812,7 @@ Variant Control::get_theme_item(Theme::DataType p_data_type, const StringName &p
|
|||
Ref<Texture2D> Control::get_editor_theme_icon(const StringName &p_name) const {
|
||||
return get_theme_icon(p_name, SNAME("EditorIcons"));
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
|
||||
ERR_READ_THREAD_GUARD_V(false);
|
||||
|
|
@ -2743,8 +2826,8 @@ bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme
|
|||
}
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_ICON, p_name, theme_types);
|
||||
}
|
||||
|
||||
|
|
@ -2760,8 +2843,8 @@ bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_t
|
|||
}
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_STYLEBOX, p_name, theme_types);
|
||||
}
|
||||
|
||||
|
|
@ -2777,8 +2860,8 @@ bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme
|
|||
}
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT, p_name, theme_types);
|
||||
}
|
||||
|
||||
|
|
@ -2794,8 +2877,8 @@ bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_
|
|||
}
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_FONT_SIZE, p_name, theme_types);
|
||||
}
|
||||
|
||||
|
|
@ -2811,8 +2894,8 @@ bool Control::has_theme_color(const StringName &p_name, const StringName &p_them
|
|||
}
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_COLOR, p_name, theme_types);
|
||||
}
|
||||
|
||||
|
|
@ -2828,8 +2911,8 @@ bool Control::has_theme_constant(const StringName &p_name, const StringName &p_t
|
|||
}
|
||||
}
|
||||
|
||||
List<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, &theme_types);
|
||||
Vector<StringName> theme_types;
|
||||
data.theme_owner->get_theme_type_dependencies(this, p_theme_type, theme_types);
|
||||
return data.theme_owner->has_theme_item_in_types(Theme::DATA_TYPE_CONSTANT, p_name, theme_types);
|
||||
}
|
||||
|
||||
|
|
@ -2837,7 +2920,7 @@ bool Control::has_theme_constant(const StringName &p_name, const StringName &p_t
|
|||
|
||||
void Control::add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
ERR_FAIL_COND(!p_icon.is_valid());
|
||||
ERR_FAIL_COND(p_icon.is_null());
|
||||
|
||||
if (data.theme_icon_override.has(p_name)) {
|
||||
data.theme_icon_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
|
||||
|
|
@ -2850,7 +2933,7 @@ void Control::add_theme_icon_override(const StringName &p_name, const Ref<Textur
|
|||
|
||||
void Control::add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
ERR_FAIL_COND(!p_style.is_valid());
|
||||
ERR_FAIL_COND(p_style.is_null());
|
||||
|
||||
if (data.theme_style_override.has(p_name)) {
|
||||
data.theme_style_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
|
||||
|
|
@ -2863,7 +2946,7 @@ void Control::add_theme_style_override(const StringName &p_name, const Ref<Style
|
|||
|
||||
void Control::add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
ERR_FAIL_COND(!p_font.is_valid());
|
||||
ERR_FAIL_COND(p_font.is_null());
|
||||
|
||||
if (data.theme_font_override.has(p_name)) {
|
||||
data.theme_font_override[p_name]->disconnect_changed(callable_mp(this, &Control::_notify_theme_override_changed));
|
||||
|
|
@ -3026,7 +3109,7 @@ void Control::set_layout_direction(Control::LayoutDirection p_direction) {
|
|||
if (data.layout_dir == p_direction) {
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_INDEX((int)p_direction, 4);
|
||||
ERR_FAIL_INDEX(p_direction, LAYOUT_DIRECTION_MAX);
|
||||
|
||||
data.layout_dir = p_direction;
|
||||
|
||||
|
|
@ -3041,11 +3124,11 @@ Control::LayoutDirection Control::get_layout_direction() const {
|
|||
bool Control::is_layout_rtl() const {
|
||||
ERR_READ_THREAD_GUARD_V(false);
|
||||
if (data.is_rtl_dirty) {
|
||||
const_cast<Control *>(this)->data.is_rtl_dirty = false;
|
||||
data.is_rtl_dirty = false;
|
||||
if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (is_part_of_edited_scene() && GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
|
||||
const_cast<Control *>(this)->data.is_rtl = true;
|
||||
data.is_rtl = true;
|
||||
return data.is_rtl;
|
||||
}
|
||||
if (is_inside_tree()) {
|
||||
|
|
@ -3053,61 +3136,68 @@ bool Control::is_layout_rtl() const {
|
|||
if (edited_scene_root == this) {
|
||||
int proj_root_layout_direction = GLOBAL_GET(SNAME("internationalization/rendering/root_node_layout_direction"));
|
||||
if (proj_root_layout_direction == 1) {
|
||||
const_cast<Control *>(this)->data.is_rtl = false;
|
||||
data.is_rtl = false;
|
||||
} else if (proj_root_layout_direction == 2) {
|
||||
const_cast<Control *>(this)->data.is_rtl = true;
|
||||
data.is_rtl = true;
|
||||
} else if (proj_root_layout_direction == 3) {
|
||||
String locale = OS::get_singleton()->get_locale();
|
||||
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
} else {
|
||||
String locale = TranslationServer::get_singleton()->get_tool_locale();
|
||||
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
}
|
||||
return data.is_rtl;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
|
||||
const_cast<Control *>(this)->data.is_rtl = true;
|
||||
data.is_rtl = true;
|
||||
return data.is_rtl;
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
Node *parent_node = get_parent();
|
||||
while (parent_node) {
|
||||
Control *parent_control = Object::cast_to<Control>(parent_node);
|
||||
if (parent_control) {
|
||||
const_cast<Control *>(this)->data.is_rtl = parent_control->is_layout_rtl();
|
||||
data.is_rtl = parent_control->is_layout_rtl();
|
||||
return data.is_rtl;
|
||||
}
|
||||
|
||||
Window *parent_window = Object::cast_to<Window>(parent_node);
|
||||
if (parent_window) {
|
||||
const_cast<Control *>(this)->data.is_rtl = parent_window->is_layout_rtl();
|
||||
data.is_rtl = parent_window->is_layout_rtl();
|
||||
return data.is_rtl;
|
||||
}
|
||||
parent_node = parent_node->get_parent();
|
||||
}
|
||||
|
||||
if (root_layout_direction == 1) {
|
||||
const_cast<Control *>(this)->data.is_rtl = false;
|
||||
data.is_rtl = false;
|
||||
} else if (root_layout_direction == 2) {
|
||||
const_cast<Control *>(this)->data.is_rtl = true;
|
||||
data.is_rtl = true;
|
||||
} else if (root_layout_direction == 3) {
|
||||
String locale = OS::get_singleton()->get_locale();
|
||||
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
} else {
|
||||
String locale = TranslationServer::get_singleton()->get_tool_locale();
|
||||
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
}
|
||||
} else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) {
|
||||
} else if (data.layout_dir == LAYOUT_DIRECTION_APPLICATION_LOCALE) {
|
||||
if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
|
||||
data.is_rtl = true;
|
||||
} else {
|
||||
String locale = TranslationServer::get_singleton()->get_tool_locale();
|
||||
data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
}
|
||||
} else if (data.layout_dir == LAYOUT_DIRECTION_SYSTEM_LOCALE) {
|
||||
if (GLOBAL_GET(SNAME("internationalization/rendering/force_right_to_left_layout_direction"))) {
|
||||
const_cast<Control *>(this)->data.is_rtl = true;
|
||||
} else {
|
||||
String locale = TranslationServer::get_singleton()->get_tool_locale();
|
||||
String locale = OS::get_singleton()->get_locale();
|
||||
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
|
||||
}
|
||||
} else {
|
||||
const_cast<Control *>(this)->data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL);
|
||||
data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL);
|
||||
}
|
||||
}
|
||||
return data.is_rtl;
|
||||
|
|
@ -3139,7 +3229,17 @@ bool Control::is_auto_translating() const {
|
|||
ERR_READ_THREAD_GUARD_V(false);
|
||||
return can_auto_translate();
|
||||
}
|
||||
#endif
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
void Control::set_tooltip_auto_translate_mode(AutoTranslateMode p_mode) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
data.tooltip_auto_translate_mode = p_mode;
|
||||
}
|
||||
|
||||
Node::AutoTranslateMode Control::get_tooltip_auto_translate_mode() const {
|
||||
ERR_READ_THREAD_GUARD_V(AUTO_TRANSLATE_MODE_INHERIT);
|
||||
return data.tooltip_auto_translate_mode;
|
||||
}
|
||||
|
||||
// Extra properties.
|
||||
|
||||
|
|
@ -3182,7 +3282,7 @@ void Control::_notification(int p_notification) {
|
|||
case NOTIFICATION_EDITOR_POST_SAVE: {
|
||||
saving = false;
|
||||
} break;
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
case NOTIFICATION_POSTINITIALIZE: {
|
||||
data.initialized = true;
|
||||
|
||||
|
|
@ -3228,7 +3328,7 @@ void Control::_notification(int p_notification) {
|
|||
case NOTIFICATION_READY: {
|
||||
#ifdef DEBUG_ENABLED
|
||||
connect(SceneStringName(ready), callable_mp(this, &Control::_clear_size_warning), CONNECT_DEFERRED | CONNECT_ONE_SHOT);
|
||||
#endif
|
||||
#endif // DEBUG_ENABLED
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_CANVAS: {
|
||||
|
|
@ -3490,6 +3590,8 @@ void Control::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_v_grow_direction", "direction"), &Control::set_v_grow_direction);
|
||||
ClassDB::bind_method(D_METHOD("get_v_grow_direction"), &Control::get_v_grow_direction);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_tooltip_auto_translate_mode", "mode"), &Control::set_tooltip_auto_translate_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_tooltip_auto_translate_mode"), &Control::get_tooltip_auto_translate_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_tooltip_text", "hint"), &Control::set_tooltip_text);
|
||||
ClassDB::bind_method(D_METHOD("get_tooltip_text"), &Control::get_tooltip_text);
|
||||
ClassDB::bind_method(D_METHOD("get_tooltip", "at_position"), &Control::get_tooltip, DEFVAL(Point2()));
|
||||
|
|
@ -3538,7 +3640,7 @@ void Control::_bind_methods() {
|
|||
#ifndef DISABLE_DEPRECATED
|
||||
ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate);
|
||||
ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating);
|
||||
#endif
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_localize_numeral_system", "enable"), &Control::set_localize_numeral_system);
|
||||
ClassDB::bind_method(D_METHOD("is_localizing_numeral_system"), &Control::is_localizing_numeral_system);
|
||||
|
|
@ -3546,7 +3648,7 @@ void Control::_bind_methods() {
|
|||
ADD_GROUP("Layout", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "custom_minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Based on Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), "set_layout_direction", "get_layout_direction");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode");
|
||||
ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION);
|
||||
|
||||
|
|
@ -3565,10 +3667,10 @@ void Control::_bind_methods() {
|
|||
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,or_less,or_greater"), "_set_anchor", "get_anchor", SIDE_BOTTOM);
|
||||
|
||||
ADD_SUBGROUP_INDENT("Anchor Offsets", "offset_", 1);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_left", PROPERTY_HINT_RANGE, "-4096,4096,suffix:px"), "set_offset", "get_offset", SIDE_LEFT);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_top", PROPERTY_HINT_RANGE, "-4096,4096,suffix:px"), "set_offset", "get_offset", SIDE_TOP);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_right", PROPERTY_HINT_RANGE, "-4096,4096,suffix:px"), "set_offset", "get_offset", SIDE_RIGHT);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_bottom", PROPERTY_HINT_RANGE, "-4096,4096,suffix:px"), "set_offset", "get_offset", SIDE_BOTTOM);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "offset_left", PROPERTY_HINT_RANGE, "-4096,4096,1,or_less,or_greater,suffix:px"), "set_offset", "get_offset", SIDE_LEFT);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "offset_top", PROPERTY_HINT_RANGE, "-4096,4096,1,or_less,or_greater,suffix:px"), "set_offset", "get_offset", SIDE_TOP);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "offset_right", PROPERTY_HINT_RANGE, "-4096,4096,1,or_less,or_greater,suffix:px"), "set_offset", "get_offset", SIDE_RIGHT);
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "offset_bottom", PROPERTY_HINT_RANGE, "-4096,4096,1,or_less,or_greater,suffix:px"), "set_offset", "get_offset", SIDE_BOTTOM);
|
||||
|
||||
ADD_SUBGROUP_INDENT("Grow Direction", "grow_", 1);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Left,Right,Both"), "set_h_grow_direction", "get_h_grow_direction");
|
||||
|
|
@ -3593,10 +3695,11 @@ void Control::_bind_methods() {
|
|||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_auto_translate", "is_auto_translating");
|
||||
#endif
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
ADD_GROUP("Tooltip", "tooltip_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "tooltip_auto_translate_mode", PROPERTY_HINT_ENUM, "Inherit,Always,Disabled"), "set_tooltip_auto_translate_mode", "get_tooltip_auto_translate_mode");
|
||||
|
||||
ADD_GROUP("Focus", "focus_");
|
||||
ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_LEFT);
|
||||
|
|
@ -3608,7 +3711,7 @@ void Control::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
|
||||
|
||||
ADD_GROUP("Mouse", "mouse_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass (Propagate Up),Ignore"), "set_mouse_filter", "get_mouse_filter");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mouse_force_pass_scroll_events"), "set_force_pass_scroll_events", "is_force_pass_scroll_events");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,I-Beam,Pointing Hand,Cross,Wait,Busy,Drag,Can Drop,Forbidden,Vertical Resize,Horizontal Resize,Secondary Diagonal Resize,Main Diagonal Resize,Move,Vertical Split,Horizontal Split,Help"), "set_default_cursor_shape", "get_default_cursor_shape");
|
||||
|
||||
|
|
@ -3694,9 +3797,14 @@ void Control::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(ANCHOR_END);
|
||||
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_INHERITED);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_APPLICATION_LOCALE);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LTR);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_RTL);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_SYSTEM_LOCALE);
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_MAX);
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
BIND_ENUM_CONSTANT(LAYOUT_DIRECTION_LOCALE);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
BIND_ENUM_CONSTANT(TEXT_DIRECTION_INHERITED);
|
||||
BIND_ENUM_CONSTANT(TEXT_DIRECTION_AUTO);
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
#include "core/math/transform_2d.h"
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
#include "core/templates/rid.h"
|
||||
#include "scene/main/canvas_item.h"
|
||||
#include "scene/main/timer.h"
|
||||
#include "scene/resources/theme.h"
|
||||
|
|
@ -49,7 +48,7 @@ class Control : public CanvasItem {
|
|||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool saving = false;
|
||||
#endif
|
||||
#endif //TOOLS_ENABLED
|
||||
|
||||
public:
|
||||
enum Anchor {
|
||||
|
|
@ -141,9 +140,14 @@ public:
|
|||
|
||||
enum LayoutDirection {
|
||||
LAYOUT_DIRECTION_INHERITED,
|
||||
LAYOUT_DIRECTION_LOCALE,
|
||||
LAYOUT_DIRECTION_APPLICATION_LOCALE,
|
||||
LAYOUT_DIRECTION_LTR,
|
||||
LAYOUT_DIRECTION_RTL
|
||||
LAYOUT_DIRECTION_RTL,
|
||||
LAYOUT_DIRECTION_SYSTEM_LOCALE,
|
||||
LAYOUT_DIRECTION_MAX,
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
LAYOUT_DIRECTION_LOCALE = LAYOUT_DIRECTION_APPLICATION_LOCALE,
|
||||
#endif // DISABLE_DEPRECATED
|
||||
};
|
||||
|
||||
enum TextDirection {
|
||||
|
|
@ -196,8 +200,8 @@ private:
|
|||
|
||||
Point2 pos_cache;
|
||||
Size2 size_cache;
|
||||
Size2 minimum_size_cache;
|
||||
bool minimum_size_valid = false;
|
||||
mutable Size2 minimum_size_cache;
|
||||
mutable bool minimum_size_valid = false;
|
||||
|
||||
Size2 last_minimum_size;
|
||||
bool updating_last_minimum_size = false;
|
||||
|
|
@ -254,14 +258,15 @@ private:
|
|||
// Internationalization.
|
||||
|
||||
LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
|
||||
bool is_rtl_dirty = true;
|
||||
bool is_rtl = false;
|
||||
mutable bool is_rtl_dirty = true;
|
||||
mutable bool is_rtl = false;
|
||||
|
||||
bool localize_numeral_system = true;
|
||||
|
||||
// Extra properties.
|
||||
|
||||
String tooltip;
|
||||
AutoTranslateMode tooltip_auto_translate_mode = AUTO_TRANSLATE_MODE_INHERIT;
|
||||
|
||||
} data;
|
||||
|
||||
|
|
@ -294,7 +299,7 @@ private:
|
|||
void _set_anchors_layout_preset(int p_preset);
|
||||
int _get_anchors_layout_preset() const;
|
||||
|
||||
void _update_minimum_size_cache();
|
||||
void _update_minimum_size_cache() const;
|
||||
void _update_minimum_size();
|
||||
void _size_changed();
|
||||
|
||||
|
|
@ -309,7 +314,7 @@ private:
|
|||
|
||||
// Focus.
|
||||
|
||||
void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest);
|
||||
void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Rect2 &p_rect, const Rect2 &p_clamp, real_t p_min, real_t &r_closest_dist_squared, Control **r_closest);
|
||||
Control *_get_focus_neighbor(Side p_side, int p_count = 0);
|
||||
|
||||
// Theming.
|
||||
|
|
@ -348,10 +353,6 @@ protected:
|
|||
void _notification(int p_notification);
|
||||
static void _bind_methods();
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
// Exposed virtual methods.
|
||||
|
||||
GDVIRTUAL1RC(bool, _has_point, Vector2)
|
||||
|
|
@ -395,8 +396,6 @@ public:
|
|||
virtual Size2 _edit_get_scale() const override;
|
||||
|
||||
virtual void _edit_set_rect(const Rect2 &p_edit_rect) override;
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
|
||||
virtual void _edit_set_rotation(real_t p_rotation) override;
|
||||
virtual real_t _edit_get_rotation() const override;
|
||||
|
|
@ -407,7 +406,13 @@ public:
|
|||
virtual bool _edit_use_pivot() const override;
|
||||
|
||||
virtual Size2 _edit_get_minimum_size() const override;
|
||||
#endif
|
||||
#endif //TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
virtual bool _edit_use_rect() const override;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
virtual void reparent(Node *p_parent, bool p_keep_global_transform = true) override;
|
||||
|
||||
// Editor integration.
|
||||
|
|
@ -417,7 +422,7 @@ public:
|
|||
PackedStringArray get_configuration_warnings() const override;
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
#endif //TOOLS_ENABLED
|
||||
|
||||
virtual bool is_text_field() const;
|
||||
|
||||
|
|
@ -604,7 +609,7 @@ public:
|
|||
Variant get_theme_item(Theme::DataType p_data_type, const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
#ifdef TOOLS_ENABLED
|
||||
Ref<Texture2D> get_editor_theme_icon(const StringName &p_name) const;
|
||||
#endif
|
||||
#endif //TOOLS_ENABLED
|
||||
|
||||
bool has_theme_icon_override(const StringName &p_name) const;
|
||||
bool has_theme_stylebox_override(const StringName &p_name) const;
|
||||
|
|
@ -636,7 +641,10 @@ public:
|
|||
#ifndef DISABLE_DEPRECATED
|
||||
void set_auto_translate(bool p_enable);
|
||||
bool is_auto_translating() const;
|
||||
#endif
|
||||
#endif //DISABLE_DEPRECATED
|
||||
|
||||
void set_tooltip_auto_translate_mode(AutoTranslateMode p_mode);
|
||||
AutoTranslateMode get_tooltip_auto_translate_mode() const;
|
||||
|
||||
// Extra properties.
|
||||
|
||||
|
|
|
|||
|
|
@ -31,9 +31,6 @@
|
|||
#include "dialogs.h"
|
||||
#include "dialogs.compat.inc"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
|
|
@ -214,7 +211,7 @@ String AcceptDialog::get_ok_button_text() const {
|
|||
|
||||
void AcceptDialog::register_text_enter(LineEdit *p_line_edit) {
|
||||
ERR_FAIL_NULL(p_line_edit);
|
||||
p_line_edit->connect("text_submitted", callable_mp(this, &AcceptDialog::_text_submitted));
|
||||
p_line_edit->connect(SceneStringName(text_submitted), callable_mp(this, &AcceptDialog::_text_submitted));
|
||||
}
|
||||
|
||||
void AcceptDialog::_update_child_rects() {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "scene/gui/check_box.h"
|
||||
#include "scene/gui/grid_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
|
|
@ -50,7 +49,7 @@ void FileDialog::popup_file_dialog() {
|
|||
}
|
||||
|
||||
void FileDialog::_focus_file_text() {
|
||||
int lp = file->get_text().rfind(".");
|
||||
int lp = file->get_text().rfind_char('.');
|
||||
if (lp != -1) {
|
||||
file->select(0, lp);
|
||||
if (file->is_inside_tree() && !is_part_of_edited_scene()) {
|
||||
|
|
@ -62,12 +61,25 @@ void FileDialog::_focus_file_text() {
|
|||
void FileDialog::_native_popup() {
|
||||
// Show native dialog directly.
|
||||
String root;
|
||||
if (access == ACCESS_RESOURCES) {
|
||||
if (!root_prefix.is_empty()) {
|
||||
root = ProjectSettings::get_singleton()->globalize_path(root_prefix);
|
||||
} else if (access == ACCESS_RESOURCES) {
|
||||
root = ProjectSettings::get_singleton()->get_resource_path();
|
||||
} else if (access == ACCESS_USERDATA) {
|
||||
root = OS::get_singleton()->get_user_data_dir();
|
||||
}
|
||||
DisplayServer::get_singleton()->file_dialog_with_options_show(get_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &FileDialog::_native_dialog_cb));
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE_EXTRA)) {
|
||||
DisplayServer::get_singleton()->file_dialog_with_options_show(get_translated_title(), ProjectSettings::get_singleton()->globalize_path(full_dir), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), processed_filters, _get_options(), callable_mp(this, &FileDialog::_native_dialog_cb_with_options));
|
||||
} else {
|
||||
DisplayServer::get_singleton()->file_dialog_show(get_translated_title(), ProjectSettings::get_singleton()->globalize_path(full_dir), file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), processed_filters, callable_mp(this, &FileDialog::_native_dialog_cb));
|
||||
}
|
||||
}
|
||||
|
||||
bool FileDialog::_can_use_native_popup() {
|
||||
if (access == ACCESS_RESOURCES || access == ACCESS_USERDATA || options.size() > 0) {
|
||||
return DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE_EXTRA);
|
||||
}
|
||||
return DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE);
|
||||
}
|
||||
|
||||
void FileDialog::popup(const Rect2i &p_rect) {
|
||||
|
|
@ -76,10 +88,11 @@ void FileDialog::popup(const Rect2i &p_rect) {
|
|||
#ifdef TOOLS_ENABLED
|
||||
if (is_part_of_edited_scene()) {
|
||||
ConfirmationDialog::popup(p_rect);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
|
||||
if (_can_use_native_popup() && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
|
||||
_native_popup();
|
||||
} else {
|
||||
ConfirmationDialog::popup(p_rect);
|
||||
|
|
@ -98,7 +111,7 @@ void FileDialog::set_visible(bool p_visible) {
|
|||
}
|
||||
#endif
|
||||
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
|
||||
if (_can_use_native_popup() && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
|
||||
if (p_visible) {
|
||||
_native_popup();
|
||||
}
|
||||
|
|
@ -107,7 +120,11 @@ void FileDialog::set_visible(bool p_visible) {
|
|||
}
|
||||
}
|
||||
|
||||
void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter, const Dictionary &p_selected_options) {
|
||||
void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter) {
|
||||
_native_dialog_cb_with_options(p_ok, p_files, p_filter, Dictionary());
|
||||
}
|
||||
|
||||
void FileDialog::_native_dialog_cb_with_options(bool p_ok, const Vector<String> &p_files, int p_filter, const Dictionary &p_selected_options) {
|
||||
if (!p_ok) {
|
||||
file->set_text("");
|
||||
emit_signal(SNAME("canceled"));
|
||||
|
|
@ -124,27 +141,70 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int
|
|||
file_name = ProjectSettings::get_singleton()->localize_path(file_name);
|
||||
}
|
||||
}
|
||||
selected_options = p_selected_options;
|
||||
|
||||
String f = files[0];
|
||||
filter->select(p_filter);
|
||||
dir->set_text(f.get_base_dir());
|
||||
file->set_text(f.get_file());
|
||||
_change_dir(f.get_base_dir());
|
||||
|
||||
if (mode == FILE_MODE_OPEN_FILES) {
|
||||
emit_signal(SNAME("files_selected"), files);
|
||||
} else {
|
||||
if (mode == FILE_MODE_SAVE_FILE) {
|
||||
if (p_filter >= 0 && p_filter < filters.size()) {
|
||||
bool valid = false;
|
||||
String flt = filters[p_filter].get_slice(";", 0);
|
||||
int filter_slice_count = flt.get_slice_count(",");
|
||||
for (int j = 0; j < filter_slice_count; j++) {
|
||||
String str = (flt.get_slice(",", j).strip_edges());
|
||||
if (f.match(str)) {
|
||||
valid = true;
|
||||
bool valid = false;
|
||||
|
||||
if (p_filter == filter->get_item_count() - 1) {
|
||||
valid = true; // Match none.
|
||||
} else if (filters.size() > 1 && p_filter == 0) {
|
||||
// Match all filters.
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
String flt = filters[i].get_slice(";", 0);
|
||||
for (int j = 0; j < flt.get_slice_count(","); j++) {
|
||||
String str = flt.get_slice(",", j).strip_edges();
|
||||
if (f.matchn(str)) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid && filter_slice_count > 0) {
|
||||
String str = (flt.get_slice(",", 0).strip_edges());
|
||||
f += str.substr(1, str.length() - 1);
|
||||
} else {
|
||||
int idx = p_filter;
|
||||
if (filters.size() > 1) {
|
||||
idx--;
|
||||
}
|
||||
if (idx >= 0 && idx < filters.size()) {
|
||||
String flt = filters[idx].get_slice(";", 0);
|
||||
int filter_slice_count = flt.get_slice_count(",");
|
||||
for (int j = 0; j < filter_slice_count; j++) {
|
||||
String str = (flt.get_slice(",", j).strip_edges());
|
||||
if (f.matchn(str)) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid && filter_slice_count > 0) {
|
||||
String str = (flt.get_slice(",", 0).strip_edges());
|
||||
f += str.substr(1, str.length() - 1);
|
||||
file->set_text(f.get_file());
|
||||
valid = true;
|
||||
}
|
||||
} else {
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Add first extension of filter if no valid extension is found.
|
||||
if (!valid) {
|
||||
int idx = p_filter;
|
||||
String flt = filters[idx].get_slice(";", 0);
|
||||
String ext = flt.get_slice(",", 0).strip_edges().get_extension();
|
||||
f += "." + ext;
|
||||
}
|
||||
emit_signal(SNAME("file_selected"), f);
|
||||
} else if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) {
|
||||
|
|
@ -153,10 +213,6 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int
|
|||
emit_signal(SNAME("dir_selected"), f);
|
||||
}
|
||||
}
|
||||
file->set_text(f);
|
||||
dir->set_text(f.get_base_dir());
|
||||
selected_options = p_selected_options;
|
||||
filter->select(p_filter);
|
||||
}
|
||||
|
||||
VBoxContainer *FileDialog::get_vbox() {
|
||||
|
|
@ -180,7 +236,7 @@ void FileDialog::_notification(int p_what) {
|
|||
#endif
|
||||
|
||||
// Replace the built-in dialog with the native one if it started visible.
|
||||
if (is_visible() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
|
||||
if (is_visible() && _can_use_native_popup() && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
|
||||
ConfirmationDialog::set_visible(false);
|
||||
_native_popup();
|
||||
}
|
||||
|
|
@ -195,17 +251,18 @@ void FileDialog::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
dir_up->set_icon(theme_cache.parent_folder);
|
||||
dir_up->set_button_icon(theme_cache.parent_folder);
|
||||
if (vbox->is_layout_rtl()) {
|
||||
dir_prev->set_icon(theme_cache.forward_folder);
|
||||
dir_next->set_icon(theme_cache.back_folder);
|
||||
dir_prev->set_button_icon(theme_cache.forward_folder);
|
||||
dir_next->set_button_icon(theme_cache.back_folder);
|
||||
} else {
|
||||
dir_prev->set_icon(theme_cache.back_folder);
|
||||
dir_next->set_icon(theme_cache.forward_folder);
|
||||
dir_prev->set_button_icon(theme_cache.back_folder);
|
||||
dir_next->set_button_icon(theme_cache.forward_folder);
|
||||
}
|
||||
refresh->set_icon(theme_cache.reload);
|
||||
show_hidden->set_icon(theme_cache.toggle_hidden);
|
||||
makedir->set_icon(theme_cache.create_folder);
|
||||
refresh->set_button_icon(theme_cache.reload);
|
||||
show_hidden->set_button_icon(theme_cache.toggle_hidden);
|
||||
makedir->set_button_icon(theme_cache.create_folder);
|
||||
show_filename_filter_button->set_button_icon(theme_cache.toggle_filename_filter);
|
||||
|
||||
dir_up->begin_bulk_theme_override();
|
||||
dir_up->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color);
|
||||
|
|
@ -218,14 +275,14 @@ void FileDialog::_notification(int p_what) {
|
|||
dir_prev->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color);
|
||||
dir_prev->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color);
|
||||
dir_prev->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color);
|
||||
dir_prev->add_theme_color_override("icon_color_pressed", theme_cache.icon_pressed_color);
|
||||
dir_prev->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color);
|
||||
dir_prev->end_bulk_theme_override();
|
||||
|
||||
dir_next->begin_bulk_theme_override();
|
||||
dir_next->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color);
|
||||
dir_next->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color);
|
||||
dir_next->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color);
|
||||
dir_next->add_theme_color_override("icon_color_pressed", theme_cache.icon_pressed_color);
|
||||
dir_next->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color);
|
||||
dir_next->end_bulk_theme_override();
|
||||
|
||||
refresh->begin_bulk_theme_override();
|
||||
|
|
@ -249,6 +306,13 @@ void FileDialog::_notification(int p_what) {
|
|||
makedir->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color);
|
||||
makedir->end_bulk_theme_override();
|
||||
|
||||
show_filename_filter_button->begin_bulk_theme_override();
|
||||
show_filename_filter_button->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color);
|
||||
show_filename_filter_button->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color);
|
||||
show_filename_filter_button->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color);
|
||||
show_filename_filter_button->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color);
|
||||
show_filename_filter_button->end_bulk_theme_override();
|
||||
|
||||
invalidate();
|
||||
} break;
|
||||
|
||||
|
|
@ -274,6 +338,14 @@ void FileDialog::shortcut_input(const Ref<InputEvent> &p_event) {
|
|||
handled = false;
|
||||
}
|
||||
|
||||
} break;
|
||||
case Key::F: {
|
||||
if (k->is_command_or_control_pressed()) {
|
||||
show_filename_filter_button->set_pressed(!show_filename_filter_button->is_pressed());
|
||||
} else {
|
||||
handled = false;
|
||||
}
|
||||
|
||||
} break;
|
||||
case Key::F5: {
|
||||
invalidate();
|
||||
|
|
@ -332,6 +404,7 @@ Vector<String> FileDialog::get_selected_files() const {
|
|||
}
|
||||
|
||||
void FileDialog::update_dir() {
|
||||
full_dir = dir_access->get_current_dir();
|
||||
if (root_prefix.is_empty()) {
|
||||
dir->set_text(dir_access->get_current_dir(false));
|
||||
} else {
|
||||
|
|
@ -354,7 +427,17 @@ void FileDialog::update_dir() {
|
|||
}
|
||||
|
||||
void FileDialog::_dir_submitted(String p_dir) {
|
||||
_change_dir(root_prefix.path_join(p_dir));
|
||||
String new_dir = p_dir;
|
||||
#ifdef WINDOWS_ENABLED
|
||||
if (root_prefix.is_empty() && drives->is_visible() && !new_dir.is_network_share_path() && new_dir.is_absolute_path() && new_dir.find(":/") == -1 && new_dir.find(":\\") == -1) {
|
||||
// Non network path without X:/ prefix on Windows, add drive letter.
|
||||
new_dir = drives->get_item_text(drives->get_selected()).path_join(new_dir);
|
||||
}
|
||||
#endif
|
||||
if (!root_prefix.is_empty()) {
|
||||
new_dir = root_prefix.path_join(new_dir);
|
||||
}
|
||||
_change_dir(new_dir);
|
||||
file->set_text("");
|
||||
_push_history();
|
||||
}
|
||||
|
|
@ -425,7 +508,7 @@ void FileDialog::_action_pressed() {
|
|||
String file_text = file->get_text();
|
||||
String f = file_text.is_absolute_path() ? file_text : dir_access->get_current_dir().path_join(file_text);
|
||||
|
||||
if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) {
|
||||
if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && (dir_access->file_exists(f) || dir_access->is_bundle(f))) {
|
||||
emit_signal(SNAME("file_selected"), f);
|
||||
hide();
|
||||
} else if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_DIR) {
|
||||
|
|
@ -448,14 +531,14 @@ void FileDialog::_action_pressed() {
|
|||
bool valid = false;
|
||||
|
||||
if (filter->get_selected() == filter->get_item_count() - 1) {
|
||||
valid = true; // match none
|
||||
valid = true; // Match none.
|
||||
} else if (filters.size() > 1 && filter->get_selected() == 0) {
|
||||
// match all filters
|
||||
// Match all filters.
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
String flt = filters[i].get_slice(";", 0);
|
||||
for (int j = 0; j < flt.get_slice_count(","); j++) {
|
||||
String str = flt.get_slice(",", j).strip_edges();
|
||||
if (f.match(str)) {
|
||||
if (f.matchn(str)) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -471,16 +554,16 @@ void FileDialog::_action_pressed() {
|
|||
}
|
||||
if (idx >= 0 && idx < filters.size()) {
|
||||
String flt = filters[idx].get_slice(";", 0);
|
||||
int filterSliceCount = flt.get_slice_count(",");
|
||||
for (int j = 0; j < filterSliceCount; j++) {
|
||||
int filter_slice_count = flt.get_slice_count(",");
|
||||
for (int j = 0; j < filter_slice_count; j++) {
|
||||
String str = (flt.get_slice(",", j).strip_edges());
|
||||
if (f.match(str)) {
|
||||
if (f.matchn(str)) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid && filterSliceCount > 0) {
|
||||
if (!valid && filter_slice_count > 0) {
|
||||
String str = (flt.get_slice(",", 0).strip_edges());
|
||||
f += str.substr(1, str.length() - 1);
|
||||
file->set_text(f.get_file());
|
||||
|
|
@ -497,7 +580,7 @@ void FileDialog::_action_pressed() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (dir_access->file_exists(f)) {
|
||||
if (dir_access->file_exists(f) || dir_access->is_bundle(f)) {
|
||||
confirm_save->set_text(vformat(atr(ETR("File \"%s\" already exists.\nDo you want to overwrite it?")), f));
|
||||
confirm_save->popup_centered(Size2(250, 80));
|
||||
} else {
|
||||
|
|
@ -584,8 +667,10 @@ void FileDialog::deselect_all() {
|
|||
set_ok_button_text(ETR("Select Current Folder"));
|
||||
break;
|
||||
case FILE_MODE_OPEN_ANY:
|
||||
set_ok_button_text(ETR("Open"));
|
||||
break;
|
||||
case FILE_MODE_SAVE_FILE:
|
||||
// FIXME: Implement, or refactor to avoid duplication with set_mode
|
||||
set_ok_button_text(ETR("Save"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -604,8 +689,16 @@ void FileDialog::_tree_selected() {
|
|||
|
||||
if (!d["dir"]) {
|
||||
file->set_text(d["name"]);
|
||||
} else if (mode == FILE_MODE_OPEN_DIR) {
|
||||
set_ok_button_text(ETR("Select This Folder"));
|
||||
if (mode == FILE_MODE_SAVE_FILE) {
|
||||
set_ok_button_text(ETR("Save"));
|
||||
} else {
|
||||
set_ok_button_text(ETR("Open"));
|
||||
}
|
||||
} else if (mode == FILE_MODE_OPEN_DIR || mode == FILE_MODE_OPEN_ANY || !dir_access->file_exists(file->get_text())) {
|
||||
file->set_text("");
|
||||
if (mode == FILE_MODE_OPEN_DIR || mode == FILE_MODE_OPEN_ANY) {
|
||||
set_ok_button_text(ETR("Select This Folder"));
|
||||
}
|
||||
}
|
||||
|
||||
get_ok_button()->set_disabled(_is_open_should_be_disabled());
|
||||
|
|
@ -649,6 +742,74 @@ void FileDialog::update_file_name() {
|
|||
}
|
||||
}
|
||||
|
||||
void FileDialog::_item_menu_id_pressed(int p_option) {
|
||||
switch (p_option) {
|
||||
case ITEM_MENU_SHOW_IN_EXPLORER: {
|
||||
TreeItem *ti = tree->get_selected();
|
||||
String path;
|
||||
if (ti) {
|
||||
Dictionary d = ti->get_metadata(0);
|
||||
path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir().path_join(d["name"]));
|
||||
} else {
|
||||
path = ProjectSettings::get_singleton()->globalize_path(dir_access->get_current_dir());
|
||||
}
|
||||
|
||||
OS::get_singleton()->shell_show_in_file_manager(path, true);
|
||||
} break;
|
||||
|
||||
case ITEM_MENU_SHOW_BUNDLE_CONTENT: {
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
Dictionary d = ti->get_metadata(0);
|
||||
_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("");
|
||||
}
|
||||
_push_history();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void FileDialog::_empty_clicked(const Vector2 &p_pos, MouseButton p_button) {
|
||||
if (p_button == MouseButton::RIGHT) {
|
||||
item_menu->clear();
|
||||
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
|
||||
// Opening the system file manager is not supported on the Android and web editors.
|
||||
item_menu->add_item(ETR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER);
|
||||
|
||||
item_menu->set_position(tree->get_screen_position() + p_pos);
|
||||
item_menu->reset_size();
|
||||
item_menu->popup();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void FileDialog::_rmb_select(const Vector2 &p_pos, MouseButton p_button) {
|
||||
if (p_button == MouseButton::RIGHT) {
|
||||
item_menu->clear();
|
||||
#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
|
||||
// Opening the system file manager is not supported on the Android and web editors.
|
||||
TreeItem *ti = tree->get_selected();
|
||||
if (!ti) {
|
||||
return;
|
||||
}
|
||||
Dictionary d = ti->get_metadata(0);
|
||||
if (d["bundle"]) {
|
||||
item_menu->add_item(ETR("Show Package Contents"), ITEM_MENU_SHOW_BUNDLE_CONTENT);
|
||||
}
|
||||
item_menu->add_item(ETR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER);
|
||||
|
||||
item_menu->set_position(tree->get_screen_position() + p_pos);
|
||||
item_menu->reset_size();
|
||||
item_menu->popup();
|
||||
#endif
|
||||
} else {
|
||||
_tree_selected();
|
||||
}
|
||||
}
|
||||
|
||||
void FileDialog::update_file_list() {
|
||||
tree->clear();
|
||||
|
||||
|
|
@ -692,21 +853,7 @@ void FileDialog::update_file_list() {
|
|||
dirs.sort_custom<FileNoCaseComparator>();
|
||||
files.sort_custom<FileNoCaseComparator>();
|
||||
|
||||
while (!dirs.is_empty()) {
|
||||
String &dir_name = dirs.front()->get();
|
||||
TreeItem *ti = tree->create_item(root);
|
||||
ti->set_text(0, dir_name);
|
||||
ti->set_icon(0, theme_cache.folder);
|
||||
ti->set_icon_modulate(0, theme_cache.folder_icon_color);
|
||||
|
||||
Dictionary d;
|
||||
d["name"] = dir_name;
|
||||
d["dir"] = true;
|
||||
|
||||
ti->set_metadata(0, d);
|
||||
|
||||
dirs.pop_front();
|
||||
}
|
||||
String filename_filter_lower = file_name_filter.to_lower();
|
||||
|
||||
List<String> patterns;
|
||||
// build filter
|
||||
|
|
@ -734,6 +881,40 @@ void FileDialog::update_file_list() {
|
|||
}
|
||||
}
|
||||
|
||||
while (!dirs.is_empty()) {
|
||||
const String &dir_name = dirs.front()->get();
|
||||
|
||||
bool bundle = dir_access->is_bundle(dir_name);
|
||||
bool found = true;
|
||||
if (bundle) {
|
||||
bool match = patterns.is_empty();
|
||||
for (const String &E : patterns) {
|
||||
if (dir_name.matchn(E)) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
found = match;
|
||||
}
|
||||
|
||||
if (found && (filename_filter_lower.is_empty() || dir_name.to_lower().contains(filename_filter_lower))) {
|
||||
TreeItem *ti = tree->create_item(root);
|
||||
|
||||
ti->set_text(0, dir_name);
|
||||
ti->set_icon(0, theme_cache.folder);
|
||||
ti->set_icon_modulate(0, theme_cache.folder_icon_color);
|
||||
|
||||
Dictionary d;
|
||||
d["name"] = dir_name;
|
||||
d["dir"] = !bundle;
|
||||
d["bundle"] = bundle;
|
||||
|
||||
ti->set_metadata(0, d);
|
||||
}
|
||||
|
||||
dirs.pop_front();
|
||||
}
|
||||
|
||||
String base_dir = dir_access->get_current_dir();
|
||||
|
||||
while (!files.is_empty()) {
|
||||
|
|
@ -748,7 +929,7 @@ void FileDialog::update_file_list() {
|
|||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (match && (filename_filter_lower.is_empty() || files.front()->get().to_lower().contains(filename_filter_lower))) {
|
||||
TreeItem *ti = tree->create_item(root);
|
||||
ti->set_text(0, files.front()->get());
|
||||
|
||||
|
|
@ -767,6 +948,7 @@ void FileDialog::update_file_list() {
|
|||
Dictionary d;
|
||||
d["name"] = files.front()->get();
|
||||
d["dir"] = false;
|
||||
d["bundle"] = false;
|
||||
ti->set_metadata(0, d);
|
||||
|
||||
if (file->get_text() == files.front()->get() || match_str == files.front()->get()) {
|
||||
|
|
@ -781,6 +963,7 @@ void FileDialog::update_file_list() {
|
|||
// Select the first file from list if nothing is selected.
|
||||
if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) {
|
||||
tree->get_root()->get_first_child()->select(0);
|
||||
_tree_selected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -790,39 +973,134 @@ void FileDialog::_filter_selected(int) {
|
|||
update_file_list();
|
||||
}
|
||||
|
||||
void FileDialog::_filename_filter_changed() {
|
||||
update_filename_filter();
|
||||
update_file_list();
|
||||
callable_mp(this, &FileDialog::_tree_select_first).call_deferred();
|
||||
}
|
||||
|
||||
void FileDialog::_tree_select_first() {
|
||||
if (tree->get_root() && tree->get_root()->get_first_child()) {
|
||||
tree->get_root()->get_first_child()->select(0);
|
||||
_tree_selected();
|
||||
}
|
||||
}
|
||||
|
||||
void FileDialog::_filename_filter_selected() {
|
||||
TreeItem *item = tree->get_selected();
|
||||
if (item) {
|
||||
file->set_text(item->get_text(0));
|
||||
file->emit_signal(SceneStringName(text_submitted), file->get_text());
|
||||
}
|
||||
}
|
||||
|
||||
void FileDialog::update_filters() {
|
||||
filter->clear();
|
||||
processed_filters.clear();
|
||||
|
||||
if (filters.size() > 1) {
|
||||
String all_filters;
|
||||
String all_mime;
|
||||
String all_filters_full;
|
||||
String all_mime_full;
|
||||
|
||||
const int max_filters = 5;
|
||||
|
||||
// "All Recognized" display name.
|
||||
for (int i = 0; i < MIN(max_filters, filters.size()); i++) {
|
||||
String flt = filters[i].get_slice(";", 0).strip_edges();
|
||||
if (i > 0) {
|
||||
String flt = filters[i].get_slicec(';', 0).strip_edges();
|
||||
if (!all_filters.is_empty() && !flt.is_empty()) {
|
||||
all_filters += ", ";
|
||||
}
|
||||
all_filters += flt;
|
||||
|
||||
String mime = filters[i].get_slicec(';', 2).strip_edges();
|
||||
if (!all_mime.is_empty() && !mime.is_empty()) {
|
||||
all_mime += ", ";
|
||||
}
|
||||
all_mime += mime;
|
||||
}
|
||||
|
||||
// "All Recognized" filter.
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
String flt = filters[i].get_slicec(';', 0).strip_edges();
|
||||
if (!all_filters_full.is_empty() && !flt.is_empty()) {
|
||||
all_filters_full += ",";
|
||||
}
|
||||
all_filters_full += flt;
|
||||
|
||||
String mime = filters[i].get_slicec(';', 2).strip_edges();
|
||||
if (!all_mime_full.is_empty() && !mime.is_empty()) {
|
||||
all_mime_full += ",";
|
||||
}
|
||||
all_mime_full += mime;
|
||||
}
|
||||
|
||||
String native_all_name;
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE_MIME)) {
|
||||
native_all_name += all_filters;
|
||||
}
|
||||
if (!native_all_name.is_empty()) {
|
||||
native_all_name += ", ";
|
||||
}
|
||||
native_all_name += all_mime;
|
||||
|
||||
if (max_filters < filters.size()) {
|
||||
all_filters += ", ...";
|
||||
native_all_name += ", ...";
|
||||
}
|
||||
|
||||
filter->add_item(atr(ETR("All Recognized")) + " (" + all_filters + ")");
|
||||
processed_filters.push_back(all_filters_full + ";" + atr(ETR("All Recognized")) + " (" + native_all_name + ")" + ";" + all_mime_full);
|
||||
}
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
String flt = filters[i].get_slice(";", 0).strip_edges();
|
||||
String desc = filters[i].get_slice(";", 1).strip_edges();
|
||||
if (desc.length()) {
|
||||
filter->add_item(String(tr(desc)) + " (" + flt + ")");
|
||||
String flt = filters[i].get_slicec(';', 0).strip_edges();
|
||||
String desc = filters[i].get_slicec(';', 1).strip_edges();
|
||||
String mime = filters[i].get_slicec(';', 2).strip_edges();
|
||||
String native_name;
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE_MIME)) {
|
||||
native_name += flt;
|
||||
}
|
||||
if (!native_name.is_empty() && !mime.is_empty()) {
|
||||
native_name += ", ";
|
||||
}
|
||||
native_name += mime;
|
||||
if (!desc.is_empty()) {
|
||||
filter->add_item(atr(desc) + " (" + flt + ")");
|
||||
processed_filters.push_back(flt + ";" + atr(desc) + " (" + native_name + ");" + mime);
|
||||
} else {
|
||||
filter->add_item("(" + flt + ")");
|
||||
processed_filters.push_back(flt + ";(" + native_name + ");" + mime);
|
||||
}
|
||||
}
|
||||
|
||||
filter->add_item(atr(ETR("All Files")) + " (*)");
|
||||
String f = atr(ETR("All Files")) + " (*.*)";
|
||||
filter->add_item(f);
|
||||
processed_filters.push_back("*.*;" + f + ";application/octet-stream");
|
||||
}
|
||||
|
||||
void FileDialog::clear_filename_filter() {
|
||||
set_filename_filter("");
|
||||
update_filename_filter_gui();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void FileDialog::update_filename_filter_gui() {
|
||||
filename_filter_box->set_visible(show_filename_filter);
|
||||
if (!show_filename_filter) {
|
||||
file_name_filter.clear();
|
||||
}
|
||||
if (filename_filter->get_text() == file_name_filter) {
|
||||
return;
|
||||
}
|
||||
filename_filter->set_text(file_name_filter);
|
||||
}
|
||||
|
||||
void FileDialog::update_filename_filter() {
|
||||
if (filename_filter->get_text() == file_name_filter) {
|
||||
return;
|
||||
}
|
||||
set_filename_filter(filename_filter->get_text());
|
||||
}
|
||||
|
||||
void FileDialog::clear_filters() {
|
||||
|
|
@ -851,12 +1129,26 @@ void FileDialog::set_filters(const Vector<String> &p_filters) {
|
|||
invalidate();
|
||||
}
|
||||
|
||||
void FileDialog::set_filename_filter(const String &p_filename_filter) {
|
||||
if (file_name_filter == p_filename_filter) {
|
||||
return;
|
||||
}
|
||||
file_name_filter = p_filename_filter;
|
||||
update_filename_filter_gui();
|
||||
emit_signal(SNAME("filename_filter_changed"), filter);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
Vector<String> FileDialog::get_filters() const {
|
||||
return filters;
|
||||
}
|
||||
|
||||
String FileDialog::get_filename_filter() const {
|
||||
return file_name_filter;
|
||||
}
|
||||
|
||||
String FileDialog::get_current_dir() const {
|
||||
return dir->get_text();
|
||||
return full_dir;
|
||||
}
|
||||
|
||||
String FileDialog::get_current_file() const {
|
||||
|
|
@ -864,7 +1156,7 @@ String FileDialog::get_current_file() const {
|
|||
}
|
||||
|
||||
String FileDialog::get_current_path() const {
|
||||
return dir->get_text().path_join(file->get_text());
|
||||
return full_dir.path_join(file->get_text());
|
||||
}
|
||||
|
||||
void FileDialog::set_current_dir(const String &p_dir) {
|
||||
|
|
@ -887,7 +1179,7 @@ void FileDialog::set_current_path(const String &p_path) {
|
|||
if (!p_path.size()) {
|
||||
return;
|
||||
}
|
||||
int pos = MAX(p_path.rfind("/"), p_path.rfind("\\"));
|
||||
int pos = MAX(p_path.rfind_char('/'), p_path.rfind_char('\\'));
|
||||
if (pos == -1) {
|
||||
set_current_file(p_path);
|
||||
} else {
|
||||
|
|
@ -989,9 +1281,16 @@ void FileDialog::set_access(Access p_access) {
|
|||
if (access == p_access) {
|
||||
return;
|
||||
}
|
||||
access = p_access;
|
||||
root_prefix = "";
|
||||
root_subfolder = "";
|
||||
|
||||
switch (p_access) {
|
||||
case ACCESS_FILESYSTEM: {
|
||||
dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
#ifdef ANDROID_ENABLED
|
||||
set_root_subfolder(OS::get_singleton()->get_system_dir(OS::SYSTEM_DIR_DESKTOP));
|
||||
#endif
|
||||
} break;
|
||||
case ACCESS_RESOURCES: {
|
||||
dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
|
||||
|
|
@ -1000,9 +1299,6 @@ void FileDialog::set_access(Access p_access) {
|
|||
dir_access = DirAccess::create(DirAccess::ACCESS_USERDATA);
|
||||
} break;
|
||||
}
|
||||
access = p_access;
|
||||
root_prefix = "";
|
||||
root_subfolder = "";
|
||||
_update_drives();
|
||||
invalidate();
|
||||
update_filters();
|
||||
|
|
@ -1143,7 +1439,7 @@ void FileDialog::_update_option_controls() {
|
|||
CheckBox *cb = memnew(CheckBox);
|
||||
cb->set_pressed(opt.default_idx);
|
||||
grid_options->add_child(cb);
|
||||
cb->connect("toggled", callable_mp(this, &FileDialog::_option_changed_checkbox_toggled).bind(opt.name));
|
||||
cb->connect(SceneStringName(toggled), callable_mp(this, &FileDialog::_option_changed_checkbox_toggled).bind(opt.name));
|
||||
selected_options[opt.name] = (bool)opt.default_idx;
|
||||
} else {
|
||||
OptionButton *ob = memnew(OptionButton);
|
||||
|
|
@ -1264,6 +1560,9 @@ void FileDialog::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("add_filter", "filter", "description"), &FileDialog::add_filter, DEFVAL(""));
|
||||
ClassDB::bind_method(D_METHOD("set_filters", "filters"), &FileDialog::set_filters);
|
||||
ClassDB::bind_method(D_METHOD("get_filters"), &FileDialog::get_filters);
|
||||
ClassDB::bind_method(D_METHOD("clear_filename_filter"), &FileDialog::clear_filename_filter);
|
||||
ClassDB::bind_method(D_METHOD("set_filename_filter", "filter"), &FileDialog::set_filename_filter);
|
||||
ClassDB::bind_method(D_METHOD("get_filename_filter"), &FileDialog::get_filename_filter);
|
||||
ClassDB::bind_method(D_METHOD("get_option_name", "option"), &FileDialog::get_option_name);
|
||||
ClassDB::bind_method(D_METHOD("get_option_values", "option"), &FileDialog::get_option_values);
|
||||
ClassDB::bind_method(D_METHOD("get_option_default", "option"), &FileDialog::get_option_default);
|
||||
|
|
@ -1303,6 +1602,7 @@ void FileDialog::_bind_methods() {
|
|||
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::STRING, "filename_filter"), "set_filename_filter", "get_filename_filter");
|
||||
ADD_ARRAY_COUNT("Options", "option_count", "set_option_count", "get_option_count", "option_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_native_dialog"), "set_use_native_dialog", "get_use_native_dialog");
|
||||
|
|
@ -1313,6 +1613,7 @@ void FileDialog::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path")));
|
||||
ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths")));
|
||||
ADD_SIGNAL(MethodInfo("dir_selected", PropertyInfo(Variant::STRING, "dir")));
|
||||
ADD_SIGNAL(MethodInfo("filename_filter_changed", PropertyInfo(Variant::STRING, "filter")));
|
||||
|
||||
BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILE);
|
||||
BIND_ENUM_CONSTANT(FILE_MODE_OPEN_FILES);
|
||||
|
|
@ -1330,6 +1631,7 @@ void FileDialog::_bind_methods() {
|
|||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, reload);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, toggle_hidden);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, folder);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, toggle_filename_filter);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, file);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FileDialog, create_folder);
|
||||
|
||||
|
|
@ -1361,6 +1663,26 @@ void FileDialog::set_show_hidden_files(bool p_show) {
|
|||
invalidate();
|
||||
}
|
||||
|
||||
void FileDialog::set_show_filename_filter(bool p_show) {
|
||||
if (p_show == show_filename_filter) {
|
||||
return;
|
||||
}
|
||||
if (p_show) {
|
||||
filename_filter->grab_focus();
|
||||
} else {
|
||||
if (filename_filter->has_focus()) {
|
||||
tree->call_deferred("grab_focus");
|
||||
}
|
||||
}
|
||||
show_filename_filter = p_show;
|
||||
update_filename_filter_gui();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
bool FileDialog::get_show_filename_filter() const {
|
||||
return show_filename_filter;
|
||||
}
|
||||
|
||||
bool FileDialog::is_showing_hidden_files() const {
|
||||
return show_hidden_files;
|
||||
}
|
||||
|
|
@ -1379,7 +1701,7 @@ void FileDialog::set_use_native_dialog(bool p_native) {
|
|||
#endif
|
||||
|
||||
// Replace the built-in dialog with the native one if it's currently visible.
|
||||
if (is_visible() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG_FILE) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
|
||||
if (is_inside_tree() && is_visible() && _can_use_native_popup() && (use_native_dialog || OS::get_singleton()->is_sandboxed())) {
|
||||
ConfirmationDialog::set_visible(false);
|
||||
_native_popup();
|
||||
}
|
||||
|
|
@ -1401,13 +1723,13 @@ FileDialog::FileDialog() {
|
|||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
|
||||
dir_prev = memnew(Button);
|
||||
dir_prev->set_theme_type_variation("FlatButton");
|
||||
dir_prev->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
dir_prev->set_tooltip_text(ETR("Go to previous folder."));
|
||||
dir_next = memnew(Button);
|
||||
dir_next->set_theme_type_variation("FlatButton");
|
||||
dir_next->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
dir_next->set_tooltip_text(ETR("Go to next folder."));
|
||||
dir_up = memnew(Button);
|
||||
dir_up->set_theme_type_variation("FlatButton");
|
||||
dir_up->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
dir_up->set_tooltip_text(ETR("Go to parent folder."));
|
||||
hbc->add_child(dir_prev);
|
||||
hbc->add_child(dir_next);
|
||||
|
|
@ -1431,30 +1753,39 @@ FileDialog::FileDialog() {
|
|||
dir->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
refresh = memnew(Button);
|
||||
refresh->set_theme_type_variation("FlatButton");
|
||||
refresh->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
refresh->set_tooltip_text(ETR("Refresh files."));
|
||||
refresh->connect(SceneStringName(pressed), callable_mp(this, &FileDialog::update_file_list));
|
||||
hbc->add_child(refresh);
|
||||
|
||||
show_hidden = memnew(Button);
|
||||
show_hidden->set_theme_type_variation("FlatButton");
|
||||
show_hidden->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
show_hidden->set_toggle_mode(true);
|
||||
show_hidden->set_pressed(is_showing_hidden_files());
|
||||
show_hidden->set_tooltip_text(ETR("Toggle the visibility of hidden files."));
|
||||
show_hidden->connect("toggled", callable_mp(this, &FileDialog::set_show_hidden_files));
|
||||
show_hidden->connect(SceneStringName(toggled), callable_mp(this, &FileDialog::set_show_hidden_files));
|
||||
hbc->add_child(show_hidden);
|
||||
|
||||
show_filename_filter_button = memnew(Button);
|
||||
show_filename_filter_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
show_filename_filter_button->set_toggle_mode(true);
|
||||
show_filename_filter_button->set_pressed(false);
|
||||
show_filename_filter_button->set_tooltip_text(RTR("Toggle the visibility of the filter for file names."));
|
||||
show_filename_filter_button->connect(SceneStringName(toggled), callable_mp(this, &FileDialog::set_show_filename_filter));
|
||||
hbc->add_child(show_filename_filter_button);
|
||||
|
||||
shortcuts_container = memnew(HBoxContainer);
|
||||
hbc->add_child(shortcuts_container);
|
||||
|
||||
makedir = memnew(Button);
|
||||
makedir->set_theme_type_variation("FlatButton");
|
||||
makedir->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
makedir->set_tooltip_text(ETR("Create a new folder."));
|
||||
makedir->connect(SceneStringName(pressed), callable_mp(this, &FileDialog::_make_dir));
|
||||
hbc->add_child(makedir);
|
||||
vbox->add_child(hbc);
|
||||
|
||||
tree = memnew(Tree);
|
||||
tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
tree->set_hide_root(true);
|
||||
vbox->add_margin_child(ETR("Directories & Files:"), tree, true);
|
||||
|
||||
|
|
@ -1465,6 +1796,17 @@ FileDialog::FileDialog() {
|
|||
message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
|
||||
tree->add_child(message);
|
||||
|
||||
filename_filter_box = memnew(HBoxContainer);
|
||||
filename_filter_box->add_child(memnew(Label(RTR("Filter:"))));
|
||||
filename_filter = memnew(LineEdit);
|
||||
filename_filter->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
|
||||
filename_filter->set_stretch_ratio(4);
|
||||
filename_filter->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
filename_filter->set_clear_button_enabled(true);
|
||||
filename_filter_box->add_child(filename_filter);
|
||||
filename_filter_box->set_visible(false);
|
||||
vbox->add_child(filename_filter_box);
|
||||
|
||||
file_box = memnew(HBoxContainer);
|
||||
file_box->add_child(memnew(Label(ETR("File:"))));
|
||||
file = memnew(LineEdit);
|
||||
|
|
@ -1488,12 +1830,18 @@ FileDialog::FileDialog() {
|
|||
_update_drives();
|
||||
|
||||
connect(SceneStringName(confirmed), callable_mp(this, &FileDialog::_action_pressed));
|
||||
tree->set_allow_rmb_select(true);
|
||||
tree->connect("multi_selected", callable_mp(this, &FileDialog::_tree_multi_selected), CONNECT_DEFERRED);
|
||||
tree->connect("cell_selected", callable_mp(this, &FileDialog::_tree_selected), CONNECT_DEFERRED);
|
||||
tree->connect("item_activated", callable_mp(this, &FileDialog::_tree_item_activated));
|
||||
tree->connect("nothing_selected", callable_mp(this, &FileDialog::deselect_all));
|
||||
dir->connect("text_submitted", callable_mp(this, &FileDialog::_dir_submitted));
|
||||
file->connect("text_submitted", callable_mp(this, &FileDialog::_file_submitted));
|
||||
tree->connect("item_mouse_selected", callable_mp(this, &FileDialog::_rmb_select));
|
||||
tree->connect("empty_clicked", callable_mp(this, &FileDialog::_empty_clicked));
|
||||
|
||||
dir->connect(SceneStringName(text_submitted), callable_mp(this, &FileDialog::_dir_submitted));
|
||||
filename_filter->connect(SceneStringName(text_changed), callable_mp(this, &FileDialog::_filename_filter_changed).unbind(1));
|
||||
filename_filter->connect(SceneStringName(text_submitted), callable_mp(this, &FileDialog::_filename_filter_selected).unbind(1));
|
||||
file->connect(SceneStringName(text_submitted), callable_mp(this, &FileDialog::_file_submitted));
|
||||
filter->connect(SceneStringName(item_selected), callable_mp(this, &FileDialog::_filter_selected));
|
||||
|
||||
confirm_save = memnew(ConfirmationDialog);
|
||||
|
|
@ -1520,10 +1868,16 @@ FileDialog::FileDialog() {
|
|||
exterr->set_text(ETR("Invalid extension, or empty filename."));
|
||||
add_child(exterr, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
item_menu = memnew(PopupMenu);
|
||||
item_menu->connect(SceneStringName(id_pressed), callable_mp(this, &FileDialog::_item_menu_id_pressed));
|
||||
add_child(item_menu);
|
||||
|
||||
update_filters();
|
||||
update_filename_filter_gui();
|
||||
update_dir();
|
||||
|
||||
set_hide_on_ok(false);
|
||||
set_size(Size2(640, 360));
|
||||
|
||||
if (register_func) {
|
||||
register_func(this);
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "scene/property_list_helper.h"
|
||||
|
||||
class GridContainer;
|
||||
class PopupMenu;
|
||||
|
||||
class FileDialog : public ConfirmationDialog {
|
||||
GDCLASS(FileDialog, ConfirmationDialog);
|
||||
|
|
@ -59,6 +60,12 @@ public:
|
|||
FILE_MODE_SAVE_FILE
|
||||
};
|
||||
|
||||
enum ItemMenu {
|
||||
ITEM_MENU_COPY_PATH,
|
||||
ITEM_MENU_SHOW_IN_EXPLORER,
|
||||
ITEM_MENU_SHOW_BUNDLE_CONTENT,
|
||||
};
|
||||
|
||||
typedef Ref<Texture2D> (*GetIconFunc)(const String &);
|
||||
typedef void (*RegisterFunc)(FileDialog *);
|
||||
|
||||
|
|
@ -80,6 +87,8 @@ private:
|
|||
HBoxContainer *shortcuts_container = nullptr;
|
||||
OptionButton *drives = nullptr;
|
||||
Tree *tree = nullptr;
|
||||
HBoxContainer *filename_filter_box = nullptr;
|
||||
LineEdit *filename_filter = nullptr;
|
||||
HBoxContainer *file_box = nullptr;
|
||||
LineEdit *file = nullptr;
|
||||
OptionButton *filter = nullptr;
|
||||
|
|
@ -87,6 +96,7 @@ private:
|
|||
AcceptDialog *exterr = nullptr;
|
||||
Ref<DirAccess> dir_access;
|
||||
ConfirmationDialog *confirm_save = nullptr;
|
||||
PopupMenu *item_menu = nullptr;
|
||||
|
||||
Label *message = nullptr;
|
||||
|
||||
|
|
@ -96,8 +106,12 @@ private:
|
|||
|
||||
Button *refresh = nullptr;
|
||||
Button *show_hidden = nullptr;
|
||||
Button *show_filename_filter_button = nullptr;
|
||||
|
||||
Vector<String> filters;
|
||||
Vector<String> processed_filters;
|
||||
String file_name_filter;
|
||||
bool show_filename_filter = false;
|
||||
|
||||
Vector<String> local_history;
|
||||
int local_history_pos = 0;
|
||||
|
|
@ -119,6 +133,7 @@ private:
|
|||
Ref<Texture2D> back_folder;
|
||||
Ref<Texture2D> reload;
|
||||
Ref<Texture2D> toggle_hidden;
|
||||
Ref<Texture2D> toggle_filename_filter;
|
||||
Ref<Texture2D> folder;
|
||||
Ref<Texture2D> file;
|
||||
Ref<Texture2D> create_folder;
|
||||
|
|
@ -145,12 +160,19 @@ private:
|
|||
Vector<Option> options;
|
||||
Dictionary selected_options;
|
||||
bool options_dirty = false;
|
||||
String full_dir;
|
||||
|
||||
void update_dir();
|
||||
void update_file_name();
|
||||
void update_file_list();
|
||||
void update_filename_filter();
|
||||
void update_filename_filter_gui();
|
||||
void update_filters();
|
||||
|
||||
void _item_menu_id_pressed(int p_option);
|
||||
void _empty_clicked(const Vector2 &p_pos, MouseButton p_button);
|
||||
void _rmb_select(const Vector2 &p_pos, MouseButton p_button = MouseButton::RIGHT);
|
||||
|
||||
void _focus_file_text();
|
||||
|
||||
void _tree_multi_selected(Object *p_object, int p_cell, bool p_selected);
|
||||
|
|
@ -164,6 +186,9 @@ private:
|
|||
void _save_confirm_pressed();
|
||||
void _cancel_pressed();
|
||||
void _filter_selected(int);
|
||||
void _filename_filter_changed();
|
||||
void _filename_filter_selected();
|
||||
void _tree_select_first();
|
||||
void _make_dir();
|
||||
void _make_dir_confirm();
|
||||
void _go_up();
|
||||
|
|
@ -177,8 +202,10 @@ private:
|
|||
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
bool _can_use_native_popup();
|
||||
void _native_popup();
|
||||
void _native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter, const Dictionary &p_selected_options);
|
||||
void _native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter);
|
||||
void _native_dialog_cb_with_options(bool p_ok, const Vector<String> &p_files, int p_filter, const Dictionary &p_selected_options);
|
||||
|
||||
bool _is_open_should_be_disabled();
|
||||
|
||||
|
|
@ -208,6 +235,9 @@ public:
|
|||
void add_filter(const String &p_filter, const String &p_description = "");
|
||||
void set_filters(const Vector<String> &p_filters);
|
||||
Vector<String> get_filters() const;
|
||||
void clear_filename_filter();
|
||||
void set_filename_filter(const String &p_filename_filter);
|
||||
String get_filename_filter() const;
|
||||
|
||||
void set_enable_multiple_selection(bool p_enable);
|
||||
Vector<String> get_selected_files() const;
|
||||
|
|
@ -253,6 +283,8 @@ public:
|
|||
|
||||
void set_show_hidden_files(bool p_show);
|
||||
bool is_showing_hidden_files() const;
|
||||
void set_show_filename_filter(bool p_show);
|
||||
bool get_show_filename_filter() const;
|
||||
|
||||
static void set_default_show_hidden_files(bool p_show);
|
||||
|
||||
|
|
|
|||
|
|
@ -261,13 +261,14 @@ void FlowContainer::_resort() {
|
|||
}
|
||||
cached_size = (vertical ? ofs.x : ofs.y) + line_height;
|
||||
cached_line_count = lines_data.size();
|
||||
cached_line_max_child_count = lines_data.size() > 0 ? lines_data[0].child_count : 0;
|
||||
}
|
||||
|
||||
Size2 FlowContainer::get_minimum_size() const {
|
||||
Size2i minimum;
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -339,6 +340,10 @@ int FlowContainer::get_line_count() const {
|
|||
return cached_line_count;
|
||||
}
|
||||
|
||||
int FlowContainer::get_line_max_child_count() const {
|
||||
return cached_line_max_child_count;
|
||||
}
|
||||
|
||||
void FlowContainer::set_alignment(AlignmentMode p_alignment) {
|
||||
if (alignment == p_alignment) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ public:
|
|||
private:
|
||||
int cached_size = 0;
|
||||
int cached_line_count = 0;
|
||||
int cached_line_max_child_count = 0;
|
||||
int cached_items_on_last_row = 0;
|
||||
|
||||
bool vertical = false;
|
||||
bool reverse_fill = false;
|
||||
|
|
@ -74,6 +76,7 @@ protected:
|
|||
|
||||
public:
|
||||
int get_line_count() const;
|
||||
int get_line_max_child_count() const;
|
||||
|
||||
void set_alignment(AlignmentMode p_alignment);
|
||||
AlignmentMode get_alignment() const;
|
||||
|
|
|
|||
|
|
@ -42,10 +42,15 @@ PackedVector2Array GraphEdit::_get_connection_line_bind_compat_86158(const Vecto
|
|||
return get_connection_line(p_from, p_to);
|
||||
}
|
||||
|
||||
Error GraphEdit::_connect_node_bind_compat_97449(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
|
||||
return connect_node(p_from, p_from_port, p_to, p_to_port);
|
||||
}
|
||||
|
||||
void GraphEdit::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("is_arrange_nodes_button_hidden"), &GraphEdit::_is_arrange_nodes_button_hidden_bind_compat_81582);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("set_arrange_nodes_button_hidden", "enable"), &GraphEdit::_set_arrange_nodes_button_hidden_bind_compat_81582);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_connection_line", "from_node", "to_node"), &GraphEdit::_get_connection_line_bind_compat_86158);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("connect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::_connect_node_bind_compat_97449);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -259,7 +259,9 @@ PackedStringArray GraphEdit::get_configuration_warnings() const {
|
|||
return warnings;
|
||||
}
|
||||
|
||||
Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
|
||||
Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, bool p_keep_alive) {
|
||||
ERR_FAIL_NULL_V_MSG(connections_layer, FAILED, "connections_layer is missing.");
|
||||
|
||||
if (is_node_connected(p_from, p_from_port, p_to, p_to_port)) {
|
||||
return OK;
|
||||
}
|
||||
|
|
@ -270,6 +272,7 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S
|
|||
c->to_node = p_to;
|
||||
c->to_port = p_to_port;
|
||||
c->activity = 0;
|
||||
c->keep_alive = p_keep_alive;
|
||||
connections.push_back(c);
|
||||
connection_map[p_from].push_back(c);
|
||||
connection_map[p_to].push_back(c);
|
||||
|
|
@ -313,26 +316,43 @@ bool GraphEdit::is_node_connected(const StringName &p_from, int p_from_port, con
|
|||
}
|
||||
|
||||
void GraphEdit::disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port) {
|
||||
for (const List<Ref<Connection>>::Element *E = connections.front(); E; E = E->next()) {
|
||||
if (E->get()->from_node == p_from && E->get()->from_port == p_from_port && E->get()->to_node == p_to && E->get()->to_port == p_to_port) {
|
||||
connection_map[p_from].erase(E->get());
|
||||
connection_map[p_to].erase(E->get());
|
||||
E->get()->_cache.line->queue_free();
|
||||
connections.erase(E);
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
minimap->queue_redraw();
|
||||
queue_redraw();
|
||||
connections_layer->queue_redraw();
|
||||
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
|
||||
return;
|
||||
Ref<Connection> conn_to_remove;
|
||||
for (const Ref<Connection> &conn : connections) {
|
||||
if (conn->from_node == p_from && conn->from_port == p_from_port && conn->to_node == p_to && conn->to_port == p_to_port) {
|
||||
conn_to_remove = conn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn_to_remove.is_valid()) {
|
||||
connection_map[p_from].erase(conn_to_remove);
|
||||
connection_map[p_to].erase(conn_to_remove);
|
||||
conn_to_remove->_cache.line->queue_free();
|
||||
connections.erase(conn_to_remove);
|
||||
|
||||
minimap->queue_redraw();
|
||||
queue_redraw();
|
||||
connections_layer->queue_redraw();
|
||||
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
|
||||
}
|
||||
}
|
||||
|
||||
const List<Ref<GraphEdit::Connection>> &GraphEdit::get_connection_list() const {
|
||||
const Vector<Ref<GraphEdit::Connection>> &GraphEdit::get_connections() const {
|
||||
return connections;
|
||||
}
|
||||
|
||||
int GraphEdit::get_connection_count(const StringName &p_node, int p_port) {
|
||||
int count = 0;
|
||||
for (const Ref<Connection> &conn : connections) {
|
||||
if ((conn->from_node == p_node && conn->from_port == p_port) || (conn->to_node == p_node && conn->to_port == p_port)) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void GraphEdit::set_scroll_offset(const Vector2 &p_offset) {
|
||||
setting_scroll_offset = true;
|
||||
h_scrollbar->set_value(p_offset.x);
|
||||
|
|
@ -356,6 +376,8 @@ void GraphEdit::_scroll_moved(double) {
|
|||
}
|
||||
|
||||
void GraphEdit::_update_scroll_offset() {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
set_block_minimum_size_adjust(true);
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
|
|
@ -524,6 +546,8 @@ void GraphEdit::_graph_element_resize_request(const Vector2 &p_new_minsize, Node
|
|||
}
|
||||
|
||||
void GraphEdit::_graph_frame_autoshrink_changed(const Vector2 &p_new_minsize, GraphFrame *p_frame) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
_update_graph_frame(p_frame);
|
||||
|
||||
minimap->queue_redraw();
|
||||
|
|
@ -535,6 +559,7 @@ void GraphEdit::_graph_frame_autoshrink_changed(const Vector2 &p_new_minsize, Gr
|
|||
void GraphEdit::_graph_element_moved(Node *p_node) {
|
||||
GraphElement *graph_element = Object::cast_to<GraphElement>(p_node);
|
||||
ERR_FAIL_NULL(graph_element);
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
minimap->queue_redraw();
|
||||
queue_redraw();
|
||||
|
|
@ -543,6 +568,7 @@ void GraphEdit::_graph_element_moved(Node *p_node) {
|
|||
}
|
||||
|
||||
void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_node) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
GraphNode *graph_node = Object::cast_to<GraphNode>(p_node);
|
||||
ERR_FAIL_NULL(graph_node);
|
||||
|
||||
|
|
@ -558,15 +584,16 @@ void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_node) {
|
|||
}
|
||||
|
||||
void GraphEdit::_graph_node_rect_changed(GraphNode *p_node) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
// Only invalidate the cache when zooming or the node is moved/resized in graph space.
|
||||
if (panner->is_panning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Ref<Connection> &c : connection_map[p_node->get_name()]) {
|
||||
c->_cache.dirty = true;
|
||||
for (Ref<Connection> &conn : connection_map[p_node->get_name()]) {
|
||||
conn->_cache.dirty = true;
|
||||
}
|
||||
|
||||
connections_layer->queue_redraw();
|
||||
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
|
||||
|
||||
|
|
@ -623,7 +650,9 @@ void GraphEdit::add_child_notify(Node *p_child) {
|
|||
}
|
||||
graph_element->connect("raise_request", callable_mp(this, &GraphEdit::_ensure_node_order_from).bind(graph_element));
|
||||
graph_element->connect("resize_request", callable_mp(this, &GraphEdit::_graph_element_resize_request).bind(graph_element));
|
||||
graph_element->connect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
|
||||
if (connections_layer != nullptr && connections_layer->is_inside_tree()) {
|
||||
graph_element->connect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)connections_layer, &CanvasItem::queue_redraw));
|
||||
}
|
||||
graph_element->connect(SceneStringName(item_rect_changed), callable_mp((CanvasItem *)minimap, &GraphEditMinimap::queue_redraw));
|
||||
|
||||
graph_element->set_scale(Vector2(zoom, zoom));
|
||||
|
|
@ -640,6 +669,9 @@ void GraphEdit::remove_child_notify(Node *p_child) {
|
|||
minimap = nullptr;
|
||||
} else if (p_child == connections_layer) {
|
||||
connections_layer = nullptr;
|
||||
if (is_inside_tree()) {
|
||||
WARN_PRINT("GraphEdit's connection_layer removed. This should not be done. If you like to remove all GraphElements from a GraphEdit node, do not simply remove all non-internal children but check their type since the connection layer has to be kept non-internal due to technical reasons.");
|
||||
}
|
||||
}
|
||||
|
||||
if (top_layer != nullptr && is_inside_tree()) {
|
||||
|
|
@ -662,7 +694,9 @@ void GraphEdit::remove_child_notify(Node *p_child) {
|
|||
for (const Ref<Connection> &conn : connection_map[graph_node->get_name()]) {
|
||||
conn->_cache.dirty = true;
|
||||
}
|
||||
connections_layer->queue_redraw();
|
||||
if (connections_layer != nullptr && connections_layer->is_inside_tree()) {
|
||||
connections_layer->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
GraphFrame *frame = Object::cast_to<GraphFrame>(graph_element);
|
||||
|
|
@ -715,14 +749,14 @@ void GraphEdit::_update_theme_item_cache() {
|
|||
void GraphEdit::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
zoom_minus_button->set_icon(theme_cache.zoom_out);
|
||||
zoom_reset_button->set_icon(theme_cache.zoom_reset);
|
||||
zoom_plus_button->set_icon(theme_cache.zoom_in);
|
||||
zoom_minus_button->set_button_icon(theme_cache.zoom_out);
|
||||
zoom_reset_button->set_button_icon(theme_cache.zoom_reset);
|
||||
zoom_plus_button->set_button_icon(theme_cache.zoom_in);
|
||||
|
||||
toggle_snapping_button->set_icon(theme_cache.snapping_toggle);
|
||||
toggle_grid_button->set_icon(theme_cache.grid_toggle);
|
||||
minimap_button->set_icon(theme_cache.minimap_toggle);
|
||||
arrange_button->set_icon(theme_cache.layout);
|
||||
toggle_snapping_button->set_button_icon(theme_cache.snapping_toggle);
|
||||
toggle_grid_button->set_button_icon(theme_cache.grid_toggle);
|
||||
minimap_button->set_button_icon(theme_cache.minimap_toggle);
|
||||
arrange_button->set_button_icon(theme_cache.layout);
|
||||
|
||||
zoom_label->set_custom_minimum_size(Size2(48, 0) * theme_cache.base_scale);
|
||||
|
||||
|
|
@ -757,6 +791,10 @@ void GraphEdit::_notification(int p_what) {
|
|||
minimap->queue_redraw();
|
||||
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
update_warped_panning();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1271,16 +1309,16 @@ Ref<GraphEdit::Connection> GraphEdit::get_closest_connection_at_point(const Vect
|
|||
|
||||
Ref<GraphEdit::Connection> closest_connection;
|
||||
float closest_distance = p_max_distance;
|
||||
for (const Ref<Connection> &c : connections) {
|
||||
if (c->_cache.aabb.distance_to(transformed_point) > p_max_distance) {
|
||||
for (const Ref<Connection> &conn : connections) {
|
||||
if (conn->_cache.aabb.distance_to(transformed_point) > p_max_distance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<Vector2> points = get_connection_line(c->_cache.from_pos * zoom, c->_cache.to_pos * zoom);
|
||||
Vector<Vector2> points = get_connection_line(conn->_cache.from_pos * zoom, conn->_cache.to_pos * zoom);
|
||||
for (int i = 0; i < points.size() - 1; i++) {
|
||||
float distance = Geometry2D::get_distance_to_segment(transformed_point, &points[i]);
|
||||
if (distance <= lines_thickness * 0.5 + p_max_distance && distance < closest_distance) {
|
||||
closest_connection = c;
|
||||
closest_connection = conn;
|
||||
closest_distance = distance;
|
||||
}
|
||||
}
|
||||
|
|
@ -1294,15 +1332,15 @@ List<Ref<GraphEdit::Connection>> GraphEdit::get_connections_intersecting_with_re
|
|||
transformed_rect.position += get_scroll_offset();
|
||||
|
||||
List<Ref<Connection>> intersecting_connections;
|
||||
for (const Ref<Connection> &c : connections) {
|
||||
if (!c->_cache.aabb.intersects(transformed_rect)) {
|
||||
for (const Ref<Connection> &conn : connections) {
|
||||
if (!conn->_cache.aabb.intersects(transformed_rect)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<Vector2> points = get_connection_line(c->_cache.from_pos * zoom, c->_cache.to_pos * zoom);
|
||||
Vector<Vector2> points = get_connection_line(conn->_cache.from_pos * zoom, conn->_cache.to_pos * zoom);
|
||||
for (int i = 0; i < points.size() - 1; i++) {
|
||||
if (Geometry2D::segment_intersects_rect(points[i], points[i + 1], transformed_rect)) {
|
||||
intersecting_connections.push_back(c);
|
||||
intersecting_connections.push_back(conn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1334,47 +1372,49 @@ void GraphEdit::_draw_minimap_connection_line(const Vector2 &p_from_graph_positi
|
|||
|
||||
void GraphEdit::_update_connections() {
|
||||
// Collect all dead connections and remove them.
|
||||
List<List<Ref<Connection>>::Element *> dead_connections;
|
||||
LocalVector<Ref<Connection>> dead_connections;
|
||||
|
||||
for (List<Ref<Connection>>::Element *E = connections.front(); E; E = E->next()) {
|
||||
Ref<Connection> &c = E->get();
|
||||
|
||||
if (c->_cache.dirty) {
|
||||
Node *from = get_node_or_null(NodePath(c->from_node));
|
||||
for (const Ref<Connection> &conn : connections) {
|
||||
if (conn->_cache.dirty) {
|
||||
Node *from = get_node_or_null(NodePath(conn->from_node));
|
||||
GraphNode *gnode_from = Object::cast_to<GraphNode>(from);
|
||||
if (!gnode_from) {
|
||||
dead_connections.push_back(E);
|
||||
if (!gnode_from && !conn->keep_alive) {
|
||||
dead_connections.push_back(conn);
|
||||
continue;
|
||||
}
|
||||
Node *to = get_node_or_null(NodePath(c->to_node));
|
||||
Node *to = get_node_or_null(NodePath(conn->to_node));
|
||||
GraphNode *gnode_to = Object::cast_to<GraphNode>(to);
|
||||
|
||||
if (!gnode_to) {
|
||||
dead_connections.push_back(E);
|
||||
if (!gnode_to && !conn->keep_alive) {
|
||||
dead_connections.push_back(conn);
|
||||
continue;
|
||||
}
|
||||
|
||||
const Vector2 from_pos = gnode_from->get_output_port_position(c->from_port) + gnode_from->get_position_offset();
|
||||
const Vector2 to_pos = gnode_to->get_input_port_position(c->to_port) + gnode_to->get_position_offset();
|
||||
if (conn->keep_alive && (!gnode_from || !gnode_to)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Color from_color = gnode_from->get_output_port_color(c->from_port);
|
||||
const Color to_color = gnode_to->get_input_port_color(c->to_port);
|
||||
const Vector2 from_pos = gnode_from->get_output_port_position(conn->from_port) + gnode_from->get_position_offset();
|
||||
const Vector2 to_pos = gnode_to->get_input_port_position(conn->to_port) + gnode_to->get_position_offset();
|
||||
|
||||
const int from_type = gnode_from->get_output_port_type(c->from_port);
|
||||
const int to_type = gnode_to->get_input_port_type(c->to_port);
|
||||
const Color from_color = gnode_from->get_output_port_color(conn->from_port);
|
||||
const Color to_color = gnode_to->get_input_port_color(conn->to_port);
|
||||
|
||||
c->_cache.from_pos = from_pos;
|
||||
c->_cache.to_pos = to_pos;
|
||||
c->_cache.from_color = from_color;
|
||||
c->_cache.to_color = to_color;
|
||||
const int from_type = gnode_from->get_output_port_type(conn->from_port);
|
||||
const int to_type = gnode_to->get_input_port_type(conn->to_port);
|
||||
|
||||
conn->_cache.from_pos = from_pos;
|
||||
conn->_cache.to_pos = to_pos;
|
||||
conn->_cache.from_color = from_color;
|
||||
conn->_cache.to_color = to_color;
|
||||
|
||||
PackedVector2Array line_points = get_connection_line(from_pos * zoom, to_pos * zoom);
|
||||
c->_cache.line->set_points(line_points);
|
||||
conn->_cache.line->set_points(line_points);
|
||||
|
||||
Ref<ShaderMaterial> line_material = c->_cache.line->get_material();
|
||||
Ref<ShaderMaterial> line_material = conn->_cache.line->get_material();
|
||||
if (line_material.is_null()) {
|
||||
line_material.instantiate();
|
||||
c->_cache.line->set_material(line_material);
|
||||
conn->_cache.line->set_material(line_material);
|
||||
}
|
||||
|
||||
float line_width = _get_shader_line_width();
|
||||
|
|
@ -1384,31 +1424,31 @@ void GraphEdit::_update_connections() {
|
|||
line_material->set_shader_parameter("rim_color", theme_cache.connection_rim_color);
|
||||
|
||||
// Compute bounding box of the line, including the line width.
|
||||
c->_cache.aabb = Rect2(line_points[0], Vector2());
|
||||
conn->_cache.aabb = Rect2(line_points[0], Vector2());
|
||||
for (int i = 0; i < line_points.size(); i++) {
|
||||
c->_cache.aabb.expand_to(line_points[i]);
|
||||
conn->_cache.aabb.expand_to(line_points[i]);
|
||||
}
|
||||
c->_cache.aabb.grow_by(lines_thickness * 0.5);
|
||||
conn->_cache.aabb.grow_by(lines_thickness * 0.5);
|
||||
|
||||
c->_cache.dirty = false;
|
||||
conn->_cache.dirty = false;
|
||||
}
|
||||
|
||||
// Skip updating/drawing connections that are not visible.
|
||||
Rect2 viewport_rect = get_viewport_rect();
|
||||
viewport_rect.position += get_scroll_offset();
|
||||
if (!c->_cache.aabb.intersects(viewport_rect)) {
|
||||
if (!conn->_cache.aabb.intersects(viewport_rect)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Color from_color = c->_cache.from_color;
|
||||
Color to_color = c->_cache.to_color;
|
||||
Color from_color = conn->_cache.from_color;
|
||||
Color to_color = conn->_cache.to_color;
|
||||
|
||||
if (c->activity > 0) {
|
||||
from_color = from_color.lerp(theme_cache.activity_color, c->activity);
|
||||
to_color = to_color.lerp(theme_cache.activity_color, c->activity);
|
||||
if (conn->activity > 0) {
|
||||
from_color = from_color.lerp(theme_cache.activity_color, conn->activity);
|
||||
to_color = to_color.lerp(theme_cache.activity_color, conn->activity);
|
||||
}
|
||||
|
||||
if (c == hovered_connection) {
|
||||
if (conn == hovered_connection) {
|
||||
from_color = from_color.blend(theme_cache.connection_hover_tint_color);
|
||||
to_color = to_color.blend(theme_cache.connection_hover_tint_color);
|
||||
}
|
||||
|
|
@ -1417,21 +1457,25 @@ void GraphEdit::_update_connections() {
|
|||
Ref<Gradient> line_gradient = memnew(Gradient);
|
||||
|
||||
float line_width = _get_shader_line_width();
|
||||
c->_cache.line->set_width(line_width);
|
||||
if (conn == hovered_connection) {
|
||||
line_width *= 1.0f + (theme_cache.connection_hover_thickness / 100.0f);
|
||||
}
|
||||
|
||||
conn->_cache.line->set_width(line_width);
|
||||
line_gradient->set_color(0, from_color);
|
||||
line_gradient->set_color(1, to_color);
|
||||
|
||||
c->_cache.line->set_gradient(line_gradient);
|
||||
conn->_cache.line->set_gradient(line_gradient);
|
||||
}
|
||||
|
||||
for (const List<Ref<Connection>>::Element *E : dead_connections) {
|
||||
List<Ref<Connection>> &connections_from = connection_map[E->get()->from_node];
|
||||
List<Ref<Connection>> &connections_to = connection_map[E->get()->to_node];
|
||||
connections_from.erase(E->get());
|
||||
connections_to.erase(E->get());
|
||||
E->get()->_cache.line->queue_free();
|
||||
for (const Ref<Connection> &dead_conn : dead_connections) {
|
||||
List<Ref<Connection>> &connections_from = connection_map[dead_conn->from_node];
|
||||
List<Ref<Connection>> &connections_to = connection_map[dead_conn->to_node];
|
||||
connections_from.erase(dead_conn);
|
||||
connections_to.erase(dead_conn);
|
||||
dead_conn->_cache.line->queue_free();
|
||||
|
||||
connections.erase(E->get());
|
||||
connections.erase(dead_conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1583,15 +1627,15 @@ void GraphEdit::_minimap_draw() {
|
|||
}
|
||||
|
||||
// Draw node connections.
|
||||
for (const Ref<Connection> &c : connections) {
|
||||
Vector2 from_graph_position = c->_cache.from_pos * zoom - graph_offset;
|
||||
Vector2 to_graph_position = c->_cache.to_pos * zoom - graph_offset;
|
||||
Color from_color = c->_cache.from_color;
|
||||
Color to_color = c->_cache.to_color;
|
||||
for (const Ref<Connection> &conn : connections) {
|
||||
Vector2 from_graph_position = conn->_cache.from_pos * zoom - graph_offset;
|
||||
Vector2 to_graph_position = conn->_cache.to_pos * zoom - graph_offset;
|
||||
Color from_color = conn->_cache.from_color;
|
||||
Color to_color = conn->_cache.to_color;
|
||||
|
||||
if (c->activity > 0) {
|
||||
from_color = from_color.lerp(theme_cache.activity_color, c->activity);
|
||||
to_color = to_color.lerp(theme_cache.activity_color, c->activity);
|
||||
if (conn->activity > 0) {
|
||||
from_color = from_color.lerp(theme_cache.activity_color, conn->activity);
|
||||
to_color = to_color.lerp(theme_cache.activity_color, conn->activity);
|
||||
}
|
||||
|
||||
_draw_minimap_connection_line(from_graph_position, to_graph_position, from_color, to_color);
|
||||
|
|
@ -1646,24 +1690,34 @@ void GraphEdit::_draw_grid() {
|
|||
Color transparent_grid_minor = theme_cache.grid_minor;
|
||||
transparent_grid_minor.a *= CLAMP(1.0 * (zoom - 0.4), 0, 1);
|
||||
|
||||
for (int i = from_pos.x; i < from_pos.x + len.x; i++) {
|
||||
for (int j = from_pos.y; j < from_pos.y + len.y; j++) {
|
||||
Color color = transparent_grid_minor;
|
||||
// Minor dots.
|
||||
if (transparent_grid_minor.a != 0) {
|
||||
for (int i = from_pos.x; i < from_pos.x + len.x; i++) {
|
||||
for (int j = from_pos.y; j < from_pos.y + len.y; j++) {
|
||||
if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0 && ABS(j) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0 && ABS(j) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0) {
|
||||
color = theme_cache.grid_major;
|
||||
float base_offset_x = i * snapping_distance * zoom - offset.x * zoom;
|
||||
float base_offset_y = j * snapping_distance * zoom - offset.y * zoom;
|
||||
|
||||
draw_rect(Rect2(base_offset_x - 1, base_offset_y - 1, 3, 3), transparent_grid_minor);
|
||||
}
|
||||
|
||||
if (color.a == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float base_offset_x = i * snapping_distance * zoom - offset.x * zoom;
|
||||
float base_offset_y = j * snapping_distance * zoom - offset.y * zoom;
|
||||
|
||||
draw_rect(Rect2(base_offset_x - 1, base_offset_y - 1, 3, 3), color);
|
||||
}
|
||||
}
|
||||
|
||||
// Major dots.
|
||||
if (theme_cache.grid_major.a != 0) {
|
||||
for (int i = from_pos.x - from_pos.x % GRID_MINOR_STEPS_PER_MAJOR_DOT; i < from_pos.x + len.x; i += GRID_MINOR_STEPS_PER_MAJOR_DOT) {
|
||||
for (int j = from_pos.y - from_pos.y % GRID_MINOR_STEPS_PER_MAJOR_DOT; j < from_pos.y + len.y; j += GRID_MINOR_STEPS_PER_MAJOR_DOT) {
|
||||
float base_offset_x = i * snapping_distance * zoom - offset.x * zoom;
|
||||
float base_offset_y = j * snapping_distance * zoom - offset.y * zoom;
|
||||
|
||||
draw_rect(Rect2(base_offset_x - 1, base_offset_y - 1, 3, 3), theme_cache.grid_major);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1680,8 +1734,10 @@ void GraphEdit::set_selected(Node *p_child) {
|
|||
}
|
||||
|
||||
void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
ERR_FAIL_COND(p_ev.is_null());
|
||||
if (panner->gui_input(p_ev, warped_panning ? get_global_rect() : Rect2())) {
|
||||
if (panner->gui_input(p_ev, get_global_rect())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1989,6 +2045,9 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
|||
} else if (p_ev->is_action("ui_copy", true)) {
|
||||
emit_signal(SNAME("copy_nodes_request"));
|
||||
accept_event();
|
||||
} else if (p_ev->is_action("ui_cut", true)) {
|
||||
emit_signal(SNAME("cut_nodes_request"));
|
||||
accept_event();
|
||||
} else if (p_ev->is_action("ui_paste", true)) {
|
||||
emit_signal(SNAME("paste_nodes_request"));
|
||||
accept_event();
|
||||
|
|
@ -2012,6 +2071,8 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
|
|||
}
|
||||
|
||||
void GraphEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
h_scrollbar->set_value(h_scrollbar->get_value() - p_scroll_vec.x);
|
||||
v_scrollbar->set_value(v_scrollbar->get_value() - p_scroll_vec.y);
|
||||
|
||||
|
|
@ -2027,22 +2088,26 @@ void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputE
|
|||
}
|
||||
|
||||
void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
|
||||
for (Ref<Connection> &c : connection_map[p_from]) {
|
||||
if (c->from_node == p_from && c->from_port == p_from_port && c->to_node == p_to && c->to_port == p_to_port) {
|
||||
if (!Math::is_equal_approx(c->activity, p_activity)) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
for (Ref<Connection> &conn : connection_map[p_from]) {
|
||||
if (conn->from_node == p_from && conn->from_port == p_from_port && conn->to_node == p_to && conn->to_port == p_to_port) {
|
||||
if (!Math::is_equal_approx(conn->activity, p_activity)) {
|
||||
// Update only if changed.
|
||||
minimap->queue_redraw();
|
||||
c->_cache.dirty = true;
|
||||
conn->_cache.dirty = true;
|
||||
connections_layer->queue_redraw();
|
||||
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
|
||||
}
|
||||
c->activity = p_activity;
|
||||
conn->activity = p_activity;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEdit::reset_all_connection_activity() {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
bool changed = false;
|
||||
for (Ref<Connection> &conn : connections) {
|
||||
if (conn->activity > 0) {
|
||||
|
|
@ -2057,8 +2122,10 @@ void GraphEdit::reset_all_connection_activity() {
|
|||
}
|
||||
|
||||
void GraphEdit::clear_connections() {
|
||||
for (Ref<Connection> &c : connections) {
|
||||
c->_cache.line->queue_free();
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
for (Ref<Connection> &conn : connections) {
|
||||
conn->_cache.line->queue_free();
|
||||
}
|
||||
|
||||
connections.clear();
|
||||
|
|
@ -2070,7 +2137,9 @@ void GraphEdit::clear_connections() {
|
|||
}
|
||||
|
||||
void GraphEdit::force_connection_drag_end() {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
ERR_FAIL_COND_MSG(!connecting, "Drag end requested without active drag!");
|
||||
|
||||
connecting = false;
|
||||
connecting_valid = false;
|
||||
minimap->queue_redraw();
|
||||
|
|
@ -2100,6 +2169,8 @@ void GraphEdit::set_zoom(float p_zoom) {
|
|||
}
|
||||
|
||||
void GraphEdit::set_zoom_custom(float p_zoom, const Vector2 &p_center) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
p_zoom = CLAMP(p_zoom, zoom_min, zoom_max);
|
||||
if (zoom == p_zoom) {
|
||||
return;
|
||||
|
|
@ -2201,8 +2272,20 @@ void GraphEdit::remove_valid_left_disconnect_type(int p_type) {
|
|||
valid_left_disconnect_types.erase(p_type);
|
||||
}
|
||||
|
||||
void GraphEdit::set_connections(const TypedArray<Dictionary> &p_connections) {
|
||||
clear_connections();
|
||||
|
||||
bool is_editor = Engine::get_singleton()->is_editor_hint();
|
||||
|
||||
for (const Dictionary d : p_connections) {
|
||||
// Always keep the connection alive in case it is created using the inspector.
|
||||
bool keep_alive = (is_editor && d.is_empty()) || d["keep_alive"];
|
||||
connect_node(d["from_node"], d["from_port"], d["to_node"], d["to_port"], keep_alive);
|
||||
}
|
||||
}
|
||||
|
||||
TypedArray<Dictionary> GraphEdit::_get_connection_list() const {
|
||||
List<Ref<Connection>> conns = get_connection_list();
|
||||
Vector<Ref<Connection>> conns = get_connections();
|
||||
|
||||
TypedArray<Dictionary> arr;
|
||||
for (const Ref<Connection> &conn : conns) {
|
||||
|
|
@ -2211,6 +2294,7 @@ TypedArray<Dictionary> GraphEdit::_get_connection_list() const {
|
|||
d["from_port"] = conn->from_port;
|
||||
d["to_node"] = conn->to_node;
|
||||
d["to_port"] = conn->to_port;
|
||||
d["keep_alive"] = conn->keep_alive;
|
||||
arr.push_back(d);
|
||||
}
|
||||
return arr;
|
||||
|
|
@ -2224,6 +2308,7 @@ Dictionary GraphEdit::_get_closest_connection_at_point(const Vector2 &p_point, f
|
|||
ret["from_port"] = c->from_port;
|
||||
ret["to_node"] = c->to_node;
|
||||
ret["to_port"] = c->to_port;
|
||||
ret["keep_alive"] = c->keep_alive;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -2238,6 +2323,7 @@ TypedArray<Dictionary> GraphEdit::_get_connections_intersecting_with_rect(const
|
|||
d["from_port"] = conn->from_port;
|
||||
d["to_node"] = conn->to_node;
|
||||
d["to_port"] = conn->to_port;
|
||||
d["keep_alive"] = conn->keep_alive;
|
||||
arr.push_back(d);
|
||||
}
|
||||
return arr;
|
||||
|
|
@ -2262,8 +2348,8 @@ void GraphEdit::_update_zoom_label() {
|
|||
}
|
||||
|
||||
void GraphEdit::_invalidate_connection_line_cache() {
|
||||
for (Ref<Connection> &c : connections) {
|
||||
c->_cache.dirty = true;
|
||||
for (Ref<Connection> &conn : connections) {
|
||||
conn->_cache.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2508,6 +2594,8 @@ bool GraphEdit::is_showing_arrange_button() const {
|
|||
}
|
||||
|
||||
void GraphEdit::override_connections_shader(const Ref<Shader> &p_shader) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
connections_shader = p_shader;
|
||||
|
||||
_invalidate_connection_line_cache();
|
||||
|
|
@ -2526,6 +2614,8 @@ void GraphEdit::_minimap_toggled() {
|
|||
}
|
||||
|
||||
void GraphEdit::set_connection_lines_curvature(float p_curvature) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
lines_curvature = p_curvature;
|
||||
_invalidate_connection_line_cache();
|
||||
connections_layer->queue_redraw();
|
||||
|
|
@ -2537,7 +2627,9 @@ float GraphEdit::get_connection_lines_curvature() const {
|
|||
}
|
||||
|
||||
void GraphEdit::set_connection_lines_thickness(float p_thickness) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
ERR_FAIL_COND_MSG(p_thickness < 0, "Connection lines thickness must be greater than or equal to 0.");
|
||||
|
||||
if (lines_thickness == p_thickness) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -2552,6 +2644,8 @@ float GraphEdit::get_connection_lines_thickness() const {
|
|||
}
|
||||
|
||||
void GraphEdit::set_connection_lines_antialiased(bool p_antialiased) {
|
||||
ERR_FAIL_NULL_MSG(connections_layer, "connections_layer is missing.");
|
||||
|
||||
if (lines_antialiased == p_antialiased) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -2575,6 +2669,11 @@ Ref<ViewPanner> GraphEdit::get_panner() {
|
|||
|
||||
void GraphEdit::set_warped_panning(bool p_warped) {
|
||||
warped_panning = p_warped;
|
||||
update_warped_panning();
|
||||
}
|
||||
|
||||
void GraphEdit::update_warped_panning() {
|
||||
panner->setup_warped_panning(get_viewport(), warped_panning);
|
||||
}
|
||||
|
||||
void GraphEdit::arrange_nodes() {
|
||||
|
|
@ -2582,11 +2681,13 @@ void GraphEdit::arrange_nodes() {
|
|||
}
|
||||
|
||||
void GraphEdit::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("connect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::connect_node);
|
||||
ClassDB::bind_method(D_METHOD("connect_node", "from_node", "from_port", "to_node", "to_port", "keep_alive"), &GraphEdit::connect_node, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("is_node_connected", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::is_node_connected);
|
||||
ClassDB::bind_method(D_METHOD("disconnect_node", "from_node", "from_port", "to_node", "to_port"), &GraphEdit::disconnect_node);
|
||||
ClassDB::bind_method(D_METHOD("set_connection_activity", "from_node", "from_port", "to_node", "to_port", "amount"), &GraphEdit::set_connection_activity);
|
||||
ClassDB::bind_method(D_METHOD("set_connections", "connections"), &GraphEdit::set_connections);
|
||||
ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list);
|
||||
ClassDB::bind_method(D_METHOD("get_connection_count", "from_node", "from_port"), &GraphEdit::get_connection_count);
|
||||
ClassDB::bind_method(D_METHOD("get_closest_connection_at_point", "point", "max_distance"), &GraphEdit::_get_closest_connection_at_point, DEFVAL(4.0));
|
||||
ClassDB::bind_method(D_METHOD("get_connections_intersecting_with_rect", "rect"), &GraphEdit::_get_connections_intersecting_with_rect);
|
||||
ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections);
|
||||
|
|
@ -2697,6 +2798,7 @@ void GraphEdit::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_curvature"), "set_connection_lines_curvature", "get_connection_lines_curvature");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness", PROPERTY_HINT_RANGE, "0,100,0.1,suffix:px"), "set_connection_lines_thickness", "get_connection_lines_thickness");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "connection_lines_antialiased"), "set_connection_lines_antialiased", "is_connection_lines_antialiased");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "connections", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::DICTIONARY, PROPERTY_HINT_NONE, String())), "set_connections", "get_connection_list");
|
||||
|
||||
ADD_GROUP("Zoom", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom"), "set_zoom", "get_zoom");
|
||||
|
|
@ -2725,13 +2827,14 @@ void GraphEdit::_bind_methods() {
|
|||
ADD_SIGNAL(MethodInfo("connection_drag_ended"));
|
||||
|
||||
ADD_SIGNAL(MethodInfo("copy_nodes_request"));
|
||||
ADD_SIGNAL(MethodInfo("cut_nodes_request"));
|
||||
ADD_SIGNAL(MethodInfo("paste_nodes_request"));
|
||||
ADD_SIGNAL(MethodInfo("duplicate_nodes_request"));
|
||||
ADD_SIGNAL(MethodInfo("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName")));
|
||||
|
||||
ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
||||
ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
|
||||
ADD_SIGNAL(MethodInfo("frame_rect_changed", PropertyInfo(Variant::OBJECT, "frame", PROPERTY_HINT_RESOURCE_TYPE, "GraphFrame"), PropertyInfo(Variant::VECTOR2, "new_rect")));
|
||||
ADD_SIGNAL(MethodInfo("frame_rect_changed", PropertyInfo(Variant::OBJECT, "frame", PROPERTY_HINT_RESOURCE_TYPE, "GraphFrame"), PropertyInfo(Variant::RECT2, "new_rect")));
|
||||
|
||||
ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "at_position")));
|
||||
|
||||
|
|
@ -2752,6 +2855,7 @@ void GraphEdit::_bind_methods() {
|
|||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, GraphEdit, activity_color, "activity");
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, connection_hover_tint_color);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphEdit, connection_hover_thickness);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, connection_valid_target_tint_color);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, connection_rim_color);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, selection_fill);
|
||||
|
|
@ -2851,7 +2955,7 @@ GraphEdit::GraphEdit() {
|
|||
_update_zoom_label();
|
||||
|
||||
zoom_minus_button = memnew(Button);
|
||||
zoom_minus_button->set_theme_type_variation("FlatButton");
|
||||
zoom_minus_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
zoom_minus_button->set_visible(show_zoom_buttons);
|
||||
zoom_minus_button->set_tooltip_text(ETR("Zoom Out"));
|
||||
zoom_minus_button->set_focus_mode(FOCUS_NONE);
|
||||
|
|
@ -2859,7 +2963,7 @@ GraphEdit::GraphEdit() {
|
|||
zoom_minus_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_zoom_minus));
|
||||
|
||||
zoom_reset_button = memnew(Button);
|
||||
zoom_reset_button->set_theme_type_variation("FlatButton");
|
||||
zoom_reset_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
zoom_reset_button->set_visible(show_zoom_buttons);
|
||||
zoom_reset_button->set_tooltip_text(ETR("Zoom Reset"));
|
||||
zoom_reset_button->set_focus_mode(FOCUS_NONE);
|
||||
|
|
@ -2867,7 +2971,7 @@ GraphEdit::GraphEdit() {
|
|||
zoom_reset_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_zoom_reset));
|
||||
|
||||
zoom_plus_button = memnew(Button);
|
||||
zoom_plus_button->set_theme_type_variation("FlatButton");
|
||||
zoom_plus_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
zoom_plus_button->set_visible(show_zoom_buttons);
|
||||
zoom_plus_button->set_tooltip_text(ETR("Zoom In"));
|
||||
zoom_plus_button->set_focus_mode(FOCUS_NONE);
|
||||
|
|
@ -2877,7 +2981,7 @@ GraphEdit::GraphEdit() {
|
|||
// Grid controls.
|
||||
|
||||
toggle_grid_button = memnew(Button);
|
||||
toggle_grid_button->set_theme_type_variation("FlatButton");
|
||||
toggle_grid_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
toggle_grid_button->set_visible(show_grid_buttons);
|
||||
toggle_grid_button->set_toggle_mode(true);
|
||||
toggle_grid_button->set_pressed(true);
|
||||
|
|
@ -2887,7 +2991,7 @@ GraphEdit::GraphEdit() {
|
|||
toggle_grid_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_show_grid_toggled));
|
||||
|
||||
toggle_snapping_button = memnew(Button);
|
||||
toggle_snapping_button->set_theme_type_variation("FlatButton");
|
||||
toggle_snapping_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
toggle_snapping_button->set_visible(show_grid_buttons);
|
||||
toggle_snapping_button->set_toggle_mode(true);
|
||||
toggle_snapping_button->set_tooltip_text(ETR("Toggle snapping to the grid."));
|
||||
|
|
@ -2909,7 +3013,7 @@ GraphEdit::GraphEdit() {
|
|||
// Extra controls.
|
||||
|
||||
minimap_button = memnew(Button);
|
||||
minimap_button->set_theme_type_variation("FlatButton");
|
||||
minimap_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
minimap_button->set_visible(show_minimap_button);
|
||||
minimap_button->set_toggle_mode(true);
|
||||
minimap_button->set_tooltip_text(ETR("Toggle the graph minimap."));
|
||||
|
|
@ -2919,7 +3023,7 @@ GraphEdit::GraphEdit() {
|
|||
minimap_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_minimap_toggled));
|
||||
|
||||
arrange_button = memnew(Button);
|
||||
arrange_button->set_theme_type_variation("FlatButton");
|
||||
arrange_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
arrange_button->set_visible(show_arrange_button);
|
||||
arrange_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::arrange_nodes));
|
||||
arrange_button->set_focus_mode(FOCUS_NONE);
|
||||
|
|
@ -2947,5 +3051,5 @@ GraphEdit::GraphEdit() {
|
|||
|
||||
set_clip_contents(true);
|
||||
|
||||
arranger = Ref<GraphEditArranger>(memnew(GraphEditArranger(this)));
|
||||
arranger.instantiate(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ public:
|
|||
int from_port = 0;
|
||||
int to_port = 0;
|
||||
float activity = 0.0;
|
||||
bool keep_alive = true;
|
||||
|
||||
private:
|
||||
struct Cache {
|
||||
|
|
@ -149,11 +150,11 @@ public:
|
|||
private:
|
||||
struct ConnectionType {
|
||||
union {
|
||||
uint64_t key = 0;
|
||||
struct {
|
||||
uint32_t type_a;
|
||||
uint32_t type_b;
|
||||
};
|
||||
uint64_t key = 0;
|
||||
};
|
||||
|
||||
static uint32_t hash(const ConnectionType &p_conn) {
|
||||
|
|
@ -238,7 +239,7 @@ private:
|
|||
bool updating = false;
|
||||
bool awaiting_scroll_offset_update = false;
|
||||
|
||||
List<Ref<Connection>> connections;
|
||||
Vector<Ref<Connection>> connections;
|
||||
HashMap<StringName, List<Ref<Connection>>> connection_map;
|
||||
Ref<Connection> hovered_connection;
|
||||
|
||||
|
|
@ -274,6 +275,7 @@ private:
|
|||
|
||||
Color activity_color;
|
||||
Color connection_hover_tint_color;
|
||||
int connection_hover_thickness;
|
||||
Color connection_valid_target_tint_color;
|
||||
Color connection_rim_color;
|
||||
|
||||
|
|
@ -339,6 +341,7 @@ private:
|
|||
|
||||
bool is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
|
||||
|
||||
void set_connections(const TypedArray<Dictionary> &p_connections);
|
||||
TypedArray<Dictionary> _get_connection_list() const;
|
||||
Dictionary _get_closest_connection_at_point(const Vector2 &p_point, float p_max_distance = 4.0) const;
|
||||
TypedArray<Dictionary> _get_connections_intersecting_with_rect(const Rect2 &p_rect) const;
|
||||
|
|
@ -362,6 +365,7 @@ private:
|
|||
bool _is_arrange_nodes_button_hidden_bind_compat_81582() const;
|
||||
void _set_arrange_nodes_button_hidden_bind_compat_81582(bool p_enable);
|
||||
PackedVector2Array _get_connection_line_bind_compat_86158(const Vector2 &p_from, const Vector2 &p_to);
|
||||
Error _connect_node_bind_compat_97449(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
|
@ -397,13 +401,14 @@ public:
|
|||
void _update_graph_frame(GraphFrame *p_frame);
|
||||
|
||||
// Connection related methods.
|
||||
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
|
||||
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, bool keep_alive = false);
|
||||
bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
|
||||
int get_connection_count(const StringName &p_node, int p_port);
|
||||
void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
|
||||
void clear_connections();
|
||||
|
||||
void force_connection_drag_end();
|
||||
const List<Ref<Connection>> &get_connection_list() const;
|
||||
const Vector<Ref<Connection>> &get_connections() const;
|
||||
void clear_connections();
|
||||
virtual PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to) const;
|
||||
Ref<Connection> get_closest_connection_at_point(const Vector2 &p_point, float p_max_distance = 4.0) const;
|
||||
List<Ref<Connection>> get_connections_intersecting_with_rect(const Rect2 &p_rect) const;
|
||||
|
|
@ -503,6 +508,7 @@ public:
|
|||
HBoxContainer *get_menu_hbox();
|
||||
Ref<ViewPanner> get_panner();
|
||||
void set_warped_panning(bool p_warped);
|
||||
void update_warped_panning();
|
||||
|
||||
void arrange_nodes();
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ void GraphEditArranger::arrange_nodes() {
|
|||
float gap_v = 100.0f;
|
||||
float gap_h = 100.0f;
|
||||
|
||||
List<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connection_list();
|
||||
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
|
||||
|
||||
for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) {
|
||||
GraphNode *graph_element = Object::cast_to<GraphNode>(graph_edit->get_child(i));
|
||||
|
|
@ -79,6 +79,9 @@ void GraphEditArranger::arrange_nodes() {
|
|||
|
||||
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
|
||||
GraphNode *p_from = Object::cast_to<GraphNode>(node_names[connection->from_node]);
|
||||
if (!p_from) {
|
||||
continue;
|
||||
}
|
||||
if (connection->to_node == graph_element->get_name() && (p_from->is_selected() || arrange_entire_graph) && connection->to_node != connection->from_node) {
|
||||
if (!s.has(p_from->get_name())) {
|
||||
s.insert(p_from->get_name());
|
||||
|
|
@ -438,7 +441,7 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Strin
|
|||
if (p_v == p_w) {
|
||||
int min_order = MAX_ORDER;
|
||||
Ref<GraphEdit::Connection> incoming;
|
||||
List<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connection_list();
|
||||
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
|
||||
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
|
||||
if (connection->to_node == p_w) {
|
||||
ORDER(connection->from_node, r_layers);
|
||||
|
|
@ -455,7 +458,7 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Strin
|
|||
Vector2 pos_from = gnode_from->get_output_port_position(incoming->from_port) * graph_edit->get_zoom();
|
||||
Vector2 pos_to = gnode_to->get_input_port_position(incoming->to_port) * graph_edit->get_zoom();
|
||||
|
||||
// If connected block node is selected, calculate thershold or add current block to list.
|
||||
// If connected block node is selected, calculate threshold or add current block to list.
|
||||
if (gnode_from->is_selected()) {
|
||||
Vector2 connected_block_pos = r_node_positions[r_root[incoming->from_node]];
|
||||
if (connected_block_pos.y != FLT_MAX) {
|
||||
|
|
@ -469,7 +472,7 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Strin
|
|||
// This time, pick an outgoing edge and repeat as above!
|
||||
int min_order = MAX_ORDER;
|
||||
Ref<GraphEdit::Connection> outgoing;
|
||||
List<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connection_list();
|
||||
const Vector<Ref<GraphEdit::Connection>> connection_list = graph_edit->get_connections();
|
||||
for (const Ref<GraphEdit::Connection> &connection : connection_list) {
|
||||
if (connection->from_node == p_w) {
|
||||
ORDER(connection->to_node, r_layers);
|
||||
|
|
@ -486,7 +489,7 @@ float GraphEditArranger::_calculate_threshold(const StringName &p_v, const Strin
|
|||
Vector2 pos_from = gnode_from->get_output_port_position(outgoing->from_port) * graph_edit->get_zoom();
|
||||
Vector2 pos_to = gnode_to->get_input_port_position(outgoing->to_port) * graph_edit->get_zoom();
|
||||
|
||||
// If connected block node is selected, calculate thershold or add current block to list.
|
||||
// If connected block node is selected, calculate threshold or add current block to list.
|
||||
if (gnode_to->is_selected()) {
|
||||
Vector2 connected_block_pos = r_node_positions[r_root[outgoing->to_node]];
|
||||
if (connected_block_pos.y != FLT_MAX) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "graph_element.h"
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
|
|
@ -60,7 +59,7 @@ void GraphElement::_resort() {
|
|||
Size2 GraphElement::get_minimum_size() const {
|
||||
Size2 minsize;
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *child = as_sortable_control(get_child(i), SortableVisbilityMode::IGNORE);
|
||||
Control *child = as_sortable_control(get_child(i), SortableVisibilityMode::IGNORE);
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "graph_frame.h"
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/resources/style_box_flat.h"
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "graph_node.h"
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
|
@ -130,7 +129,7 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
int idx = 0;
|
||||
for (int i = 0; i < get_child_count(false); i++) {
|
||||
Control *child = as_sortable_control(get_child(i, false), SortableVisbilityMode::IGNORE);
|
||||
Control *child = as_sortable_control(get_child(i, false), SortableVisibilityMode::IGNORE);
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -208,7 +207,7 @@ void GraphNode::_resort() {
|
|||
// Avoid negative stretch space.
|
||||
stretch_diff = MAX(stretch_diff, 0);
|
||||
|
||||
available_stretch_space += stretch_diff - sb_panel->get_margin(SIDE_BOTTOM) - sb_panel->get_margin(SIDE_TOP);
|
||||
available_stretch_space += stretch_diff - sb_panel->get_margin(SIDE_BOTTOM) - sb_panel->get_margin(SIDE_TOP) - titlebar_min_size.height - sb_titlebar->get_minimum_size().height;
|
||||
|
||||
// Second pass, discard elements that can't be stretched, this will run while stretchable elements exist.
|
||||
|
||||
|
|
@ -299,7 +298,7 @@ void GraphNode::draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Co
|
|||
Ref<Texture2D> port_icon = p_left ? slot.custom_port_icon_left : slot.custom_port_icon_right;
|
||||
|
||||
Point2 icon_offset;
|
||||
if (!port_icon.is_valid()) {
|
||||
if (port_icon.is_null()) {
|
||||
port_icon = theme_cache.port;
|
||||
}
|
||||
|
||||
|
|
@ -385,7 +384,7 @@ void GraphNode::set_slot(int p_slot_index, bool p_enable_left, int p_type_left,
|
|||
|
||||
if (!p_enable_left && p_type_left == 0 && p_color_left == Color(1, 1, 1, 1) &&
|
||||
!p_enable_right && p_type_right == 0 && p_color_right == Color(1, 1, 1, 1) &&
|
||||
!p_custom_left.is_valid() && !p_custom_right.is_valid()) {
|
||||
p_custom_left.is_null() && p_custom_right.is_null()) {
|
||||
slot_table.erase(p_slot_index);
|
||||
return;
|
||||
}
|
||||
|
|
@ -658,7 +657,7 @@ void GraphNode::_port_pos_update() {
|
|||
int slot_index = 0;
|
||||
|
||||
for (int i = 0; i < get_child_count(false); i++) {
|
||||
Control *child = as_sortable_control(get_child(i, false), SortableVisbilityMode::IGNORE);
|
||||
Control *child = as_sortable_control(get_child(i, false), SortableVisibilityMode::IGNORE);
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ Size2 GridContainer::get_minimum_size() const {
|
|||
|
||||
int valid_controls_index = 0;
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
void ItemList::_shape_text(int p_idx) {
|
||||
|
|
@ -58,12 +57,12 @@ int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bo
|
|||
Item item;
|
||||
item.icon = p_texture;
|
||||
item.text = p_item;
|
||||
item.xl_text = atr(p_item);
|
||||
item.selectable = p_selectable;
|
||||
items.push_back(item);
|
||||
int item_id = items.size() - 1;
|
||||
|
||||
_shape_text(items.size() - 1);
|
||||
items.write[item_id].xl_text = _atr(item_id, p_item);
|
||||
_shape_text(item_id);
|
||||
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
|
|
@ -95,7 +94,7 @@ void ItemList::set_item_text(int p_idx, const String &p_text) {
|
|||
}
|
||||
|
||||
items.write[p_idx].text = p_text;
|
||||
items.write[p_idx].xl_text = atr(p_text);
|
||||
items.write[p_idx].xl_text = _atr(p_idx, p_text);
|
||||
_shape_text(p_idx);
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
|
|
@ -141,6 +140,24 @@ String ItemList::get_item_language(int p_idx) const {
|
|||
return items[p_idx].language;
|
||||
}
|
||||
|
||||
void ItemList::set_item_auto_translate_mode(int p_idx, AutoTranslateMode p_mode) {
|
||||
if (p_idx < 0) {
|
||||
p_idx += get_item_count();
|
||||
}
|
||||
ERR_FAIL_INDEX(p_idx, items.size());
|
||||
if (items[p_idx].auto_translate_mode != p_mode) {
|
||||
items.write[p_idx].auto_translate_mode = p_mode;
|
||||
items.write[p_idx].xl_text = _atr(p_idx, items[p_idx].text);
|
||||
_shape_text(p_idx);
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
Node::AutoTranslateMode ItemList::get_item_auto_translate_mode(int p_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_idx, items.size(), AUTO_TRANSLATE_MODE_INHERIT);
|
||||
return items[p_idx].auto_translate_mode;
|
||||
}
|
||||
|
||||
void ItemList::set_item_tooltip_enabled(int p_idx, const bool p_enabled) {
|
||||
if (p_idx < 0) {
|
||||
p_idx += get_item_count();
|
||||
|
|
@ -402,7 +419,7 @@ void ItemList::select(int p_idx, bool p_single) {
|
|||
void ItemList::deselect(int p_idx) {
|
||||
ERR_FAIL_INDEX(p_idx, items.size());
|
||||
|
||||
if (select_mode != SELECT_MULTI) {
|
||||
if (select_mode == SELECT_SINGLE) {
|
||||
items.write[p_idx].selected = false;
|
||||
current = -1;
|
||||
} else {
|
||||
|
|
@ -654,7 +671,9 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
|
|||
#define CAN_SELECT(i) (items[i].selectable && !items[i].disabled)
|
||||
#define IS_SAME_ROW(i, row) (i / current_columns == row)
|
||||
|
||||
double prev_scroll = scroll_bar->get_value();
|
||||
double prev_scroll_v = scroll_bar_v->get_value();
|
||||
double prev_scroll_h = scroll_bar_h->get_value();
|
||||
bool scroll_value_modified = false;
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (defer_select_single >= 0 && mm.is_valid()) {
|
||||
|
|
@ -729,7 +748,19 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (items[i].selectable && (!items[i].selected || allow_reselect)) {
|
||||
if (select_mode == SELECT_TOGGLE) {
|
||||
if (items[i].selectable) {
|
||||
if (items[i].selected) {
|
||||
deselect(i);
|
||||
current = i;
|
||||
emit_signal(SNAME("multi_selected"), i, false);
|
||||
} else {
|
||||
select(i, false);
|
||||
current = i;
|
||||
emit_signal(SNAME("multi_selected"), i, true);
|
||||
}
|
||||
}
|
||||
} else if (items[i].selectable && (!items[i].selected || allow_reselect)) {
|
||||
select(i, select_mode == SELECT_SINGLE || !mb->is_command_or_control_pressed());
|
||||
|
||||
if (select_mode == SELECT_SINGLE) {
|
||||
|
|
@ -756,11 +787,50 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
|
|||
emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), mb->get_button_index());
|
||||
}
|
||||
}
|
||||
if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
|
||||
scroll_bar->set_value(scroll_bar->get_value() - scroll_bar->get_page() * mb->get_factor() / 8);
|
||||
}
|
||||
if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) {
|
||||
scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * mb->get_factor() / 8);
|
||||
if (mb.is_valid()) { // Copied from ScrollContainer.
|
||||
if (mb->is_pressed()) {
|
||||
bool v_scroll_hidden = !scroll_bar_v->is_visible();
|
||||
if (mb->get_button_index() == MouseButton::WHEEL_UP) {
|
||||
// By default, the vertical orientation takes precedence. This is an exception.
|
||||
if (mb->is_shift_pressed() || v_scroll_hidden) {
|
||||
scroll_bar_h->scroll(-scroll_bar_h->get_page() / 8 * mb->get_factor());
|
||||
scroll_value_modified = true;
|
||||
} else {
|
||||
scroll_bar_v->scroll(-scroll_bar_v->get_page() / 8 * mb->get_factor());
|
||||
scroll_value_modified = true;
|
||||
}
|
||||
}
|
||||
if (mb->get_button_index() == MouseButton::WHEEL_DOWN) {
|
||||
if (mb->is_shift_pressed() || v_scroll_hidden) {
|
||||
scroll_bar_h->scroll(scroll_bar_h->get_page() / 8 * mb->get_factor());
|
||||
scroll_value_modified = true;
|
||||
} else {
|
||||
scroll_bar_v->scroll(scroll_bar_v->get_page() / 8 * mb->get_factor());
|
||||
scroll_value_modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool h_scroll_hidden = !scroll_bar_h->is_visible();
|
||||
if (mb->get_button_index() == MouseButton::WHEEL_LEFT) {
|
||||
// By default, the horizontal orientation takes precedence. This is an exception.
|
||||
if (mb->is_shift_pressed() || h_scroll_hidden) {
|
||||
scroll_bar_v->scroll(-scroll_bar_v->get_page() / 8 * mb->get_factor());
|
||||
scroll_value_modified = true;
|
||||
} else {
|
||||
scroll_bar_h->scroll(-scroll_bar_h->get_page() / 8 * mb->get_factor());
|
||||
scroll_value_modified = true;
|
||||
}
|
||||
}
|
||||
if (mb->get_button_index() == MouseButton::WHEEL_RIGHT) {
|
||||
if (mb->is_shift_pressed() || h_scroll_hidden) {
|
||||
scroll_bar_v->scroll(scroll_bar_v->get_page() / 8 * mb->get_factor());
|
||||
scroll_value_modified = true;
|
||||
} else {
|
||||
scroll_bar_h->scroll(scroll_bar_h->get_page() / 8 * mb->get_factor());
|
||||
scroll_value_modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p_event->is_pressed() && items.size() > 0) {
|
||||
|
|
@ -912,7 +982,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
} else if (p_event->is_action("ui_cancel", true)) {
|
||||
search_string = "";
|
||||
} else if (p_event->is_action("ui_select", true) && select_mode == SELECT_MULTI) {
|
||||
} else if (p_event->is_action("ui_select", true) && (select_mode == SELECT_MULTI || select_mode == SELECT_TOGGLE)) {
|
||||
if (current >= 0 && current < items.size()) {
|
||||
if (CAN_SELECT(current) && !items[current].selected) {
|
||||
select(current, false);
|
||||
|
|
@ -973,10 +1043,11 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
Ref<InputEventPanGesture> pan_gesture = p_event;
|
||||
if (pan_gesture.is_valid()) {
|
||||
scroll_bar->set_value(scroll_bar->get_value() + scroll_bar->get_page() * pan_gesture->get_delta().y / 8);
|
||||
scroll_bar_v->set_value(scroll_bar_v->get_value() + scroll_bar_v->get_page() * pan_gesture->get_delta().y / 8);
|
||||
scroll_bar_h->set_value(scroll_bar_h->get_value() + scroll_bar_h->get_page() * pan_gesture->get_delta().x / 8);
|
||||
}
|
||||
|
||||
if (scroll_bar->get_value() != prev_scroll) {
|
||||
if (scroll_value_modified && (scroll_bar_v->get_value() != prev_scroll_v || scroll_bar_h->get_value() != prev_scroll_h)) {
|
||||
accept_event(); //accept event if scroll changed
|
||||
}
|
||||
|
||||
|
|
@ -1022,7 +1093,7 @@ void ItemList::_notification(int p_what) {
|
|||
} break;
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
items.write[i].xl_text = atr(items[i].text);
|
||||
items.write[i].xl_text = _atr(i, items[i].text);
|
||||
_shape_text(i);
|
||||
}
|
||||
shape_changed = true;
|
||||
|
|
@ -1032,16 +1103,26 @@ void ItemList::_notification(int p_what) {
|
|||
case NOTIFICATION_DRAW: {
|
||||
force_update_list_size();
|
||||
|
||||
int scroll_bar_minwidth = scroll_bar->get_minimum_size().x;
|
||||
scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_bar_minwidth);
|
||||
scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
|
||||
scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, theme_cache.panel_style->get_margin(SIDE_TOP));
|
||||
scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -theme_cache.panel_style->get_margin(SIDE_BOTTOM));
|
||||
Size2 scroll_bar_h_min = scroll_bar_h->is_visible() ? scroll_bar_h->get_combined_minimum_size() : Size2();
|
||||
Size2 scroll_bar_v_min = scroll_bar_v->is_visible() ? scroll_bar_v->get_combined_minimum_size() : Size2();
|
||||
|
||||
int left_margin = is_layout_rtl() ? theme_cache.panel_style->get_margin(SIDE_RIGHT) : theme_cache.panel_style->get_margin(SIDE_LEFT);
|
||||
int right_margin = is_layout_rtl() ? theme_cache.panel_style->get_margin(SIDE_LEFT) : theme_cache.panel_style->get_margin(SIDE_RIGHT);
|
||||
|
||||
scroll_bar_v->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -scroll_bar_v_min.width - right_margin);
|
||||
scroll_bar_v->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, -right_margin);
|
||||
scroll_bar_v->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, theme_cache.panel_style->get_margin(SIDE_TOP));
|
||||
scroll_bar_v->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -scroll_bar_h_min.height - theme_cache.panel_style->get_margin(SIDE_BOTTOM));
|
||||
|
||||
scroll_bar_h->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, left_margin);
|
||||
scroll_bar_h->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, -right_margin - scroll_bar_v_min.width);
|
||||
scroll_bar_h->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -scroll_bar_h_min.height - theme_cache.panel_style->get_margin(SIDE_BOTTOM));
|
||||
scroll_bar_h->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -theme_cache.panel_style->get_margin(SIDE_BOTTOM));
|
||||
|
||||
Size2 size = get_size();
|
||||
int width = size.width - theme_cache.panel_style->get_minimum_size().width;
|
||||
if (scroll_bar->is_visible()) {
|
||||
width -= scroll_bar_minwidth;
|
||||
if (scroll_bar_v->is_visible()) {
|
||||
width -= scroll_bar_v_min.width;
|
||||
}
|
||||
|
||||
draw_style_box(theme_cache.panel_style, Rect2(Point2(), size));
|
||||
|
|
@ -1058,31 +1139,41 @@ void ItemList::_notification(int p_what) {
|
|||
}
|
||||
bool rtl = is_layout_rtl();
|
||||
|
||||
if (has_focus()) {
|
||||
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true);
|
||||
draw_style_box(theme_cache.focus_style, Rect2(Point2(), size));
|
||||
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false);
|
||||
}
|
||||
|
||||
// Ensure_selected_visible needs to be checked before we draw the list.
|
||||
if (ensure_selected_visible && current >= 0 && current < items.size()) {
|
||||
Rect2 r = items[current].rect_cache;
|
||||
int from = scroll_bar->get_value();
|
||||
int to = from + scroll_bar->get_page();
|
||||
int from_v = scroll_bar_v->get_value();
|
||||
int to_v = from_v + scroll_bar_v->get_page();
|
||||
|
||||
if (r.position.y < from) {
|
||||
scroll_bar->set_value(r.position.y);
|
||||
} else if (r.position.y + r.size.y > to) {
|
||||
scroll_bar->set_value(r.position.y + r.size.y - (to - from));
|
||||
if (r.position.y < from_v) {
|
||||
scroll_bar_v->set_value(r.position.y);
|
||||
} else if (r.position.y + r.size.y > to_v) {
|
||||
scroll_bar_v->set_value(r.position.y + r.size.y - (to_v - from_v));
|
||||
}
|
||||
int from_h = scroll_bar_h->get_value();
|
||||
int to_h = from_h + scroll_bar_h->get_page();
|
||||
|
||||
if (r.position.x < from_h) {
|
||||
scroll_bar_h->set_value(r.position.x);
|
||||
} else if (r.position.x + r.size.x > to_h) {
|
||||
scroll_bar_h->set_value(r.position.x + r.size.x - (to_h - from_h));
|
||||
}
|
||||
}
|
||||
|
||||
ensure_selected_visible = false;
|
||||
|
||||
Vector2 base_ofs = theme_cache.panel_style->get_offset();
|
||||
base_ofs.y -= int(scroll_bar->get_value());
|
||||
base_ofs.y -= int(scroll_bar_v->get_value());
|
||||
if (rtl) {
|
||||
base_ofs.x += int(scroll_bar_h->get_value());
|
||||
} else {
|
||||
base_ofs.x -= int(scroll_bar_h->get_value());
|
||||
}
|
||||
|
||||
// Define a visible frame to check against and optimize drawing.
|
||||
if (!wraparound_items) {
|
||||
size.width += (scroll_bar_h->get_max() - scroll_bar_h->get_page());
|
||||
}
|
||||
const Rect2 clip(-base_ofs, size);
|
||||
|
||||
// Do a binary search to find the first separator that is below clip_position.y.
|
||||
|
|
@ -1109,7 +1200,11 @@ void ItemList::_notification(int p_what) {
|
|||
}
|
||||
|
||||
const int y = base_ofs.y + separators[i];
|
||||
draw_line(Vector2(theme_cache.panel_style->get_margin(SIDE_LEFT), y), Vector2(width, y), theme_cache.guide_color);
|
||||
if (rtl && scroll_bar_v->is_visible()) {
|
||||
draw_line(Vector2(theme_cache.panel_style->get_margin(SIDE_LEFT) + scroll_bar_v_min.width, y), Vector2(width + theme_cache.panel_style->get_margin(SIDE_LEFT) + scroll_bar_v_min.width, y), theme_cache.guide_color);
|
||||
} else {
|
||||
draw_line(Vector2(theme_cache.panel_style->get_margin(SIDE_LEFT), y), Vector2(width + theme_cache.panel_style->get_margin(SIDE_LEFT), y), theme_cache.guide_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1137,6 +1232,8 @@ void ItemList::_notification(int p_what) {
|
|||
first_item_visible = lo;
|
||||
}
|
||||
|
||||
Rect2 cursor_rcache; // Place to save the position of the cursor and draw it after everything else.
|
||||
|
||||
// Draw visible items.
|
||||
for (int i = first_item_visible; i < items.size(); i++) {
|
||||
Rect2 rcache = items[i].rect_cache;
|
||||
|
|
@ -1153,21 +1250,29 @@ void ItemList::_notification(int p_what) {
|
|||
rcache.size.width = width - rcache.position.x;
|
||||
}
|
||||
|
||||
bool should_draw_selected_bg = items[i].selected;
|
||||
bool should_draw_selected_bg = items[i].selected && hovered != i;
|
||||
bool should_draw_hovered_selected_bg = items[i].selected && hovered == i;
|
||||
bool should_draw_hovered_bg = hovered == i && !items[i].selected;
|
||||
bool should_draw_custom_bg = items[i].custom_bg.a > 0.001;
|
||||
|
||||
if (should_draw_selected_bg || should_draw_hovered_bg || should_draw_custom_bg) {
|
||||
if (should_draw_selected_bg || should_draw_hovered_selected_bg || should_draw_hovered_bg || should_draw_custom_bg) {
|
||||
Rect2 r = rcache;
|
||||
r.position += base_ofs;
|
||||
|
||||
if (rtl) {
|
||||
r.position.x = size.width - r.position.x - r.size.x;
|
||||
r.position.x = size.width - r.position.x - r.size.x + theme_cache.panel_style->get_margin(SIDE_LEFT) - theme_cache.panel_style->get_margin(SIDE_RIGHT);
|
||||
}
|
||||
|
||||
if (should_draw_selected_bg) {
|
||||
draw_style_box(sbsel, r);
|
||||
}
|
||||
if (should_draw_hovered_selected_bg) {
|
||||
if (has_focus()) {
|
||||
draw_style_box(theme_cache.hovered_selected_focus_style, r);
|
||||
} else {
|
||||
draw_style_box(theme_cache.hovered_selected_style, r);
|
||||
}
|
||||
}
|
||||
if (should_draw_hovered_bg) {
|
||||
draw_style_box(theme_cache.hovered_style, r);
|
||||
}
|
||||
|
|
@ -1177,10 +1282,8 @@ void ItemList::_notification(int p_what) {
|
|||
}
|
||||
|
||||
Vector2 text_ofs;
|
||||
Size2 icon_size;
|
||||
if (items[i].icon.is_valid()) {
|
||||
Size2 icon_size;
|
||||
//= _adjust_to_max_size(items[i].get_icon_size(),fixed_icon_size) * icon_scale;
|
||||
|
||||
if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
|
||||
icon_size = fixed_icon_size * icon_scale;
|
||||
} else {
|
||||
|
|
@ -1192,9 +1295,9 @@ void ItemList::_notification(int p_what) {
|
|||
Point2 pos = items[i].rect_cache.position + icon_ofs + base_ofs;
|
||||
|
||||
if (icon_mode == ICON_MODE_TOP) {
|
||||
pos.y += theme_cache.v_separation / 2;
|
||||
pos.y += MAX(theme_cache.v_separation, 0) / 2;
|
||||
} else {
|
||||
pos.x += theme_cache.h_separation / 2;
|
||||
pos.x += MAX(theme_cache.h_separation, 0) / 2;
|
||||
}
|
||||
|
||||
if (icon_mode == ICON_MODE_TOP) {
|
||||
|
|
@ -1243,8 +1346,8 @@ void ItemList::_notification(int p_what) {
|
|||
}
|
||||
|
||||
Point2 draw_pos = items[i].rect_cache.position;
|
||||
draw_pos.x += theme_cache.h_separation / 2;
|
||||
draw_pos.y += theme_cache.v_separation / 2;
|
||||
draw_pos.x += MAX(theme_cache.h_separation, 0) / 2;
|
||||
draw_pos.y += MAX(theme_cache.v_separation, 0) / 2;
|
||||
if (rtl) {
|
||||
draw_pos.x = size.width - draw_pos.x - tag_icon_size.x;
|
||||
}
|
||||
|
|
@ -1253,19 +1356,12 @@ void ItemList::_notification(int p_what) {
|
|||
}
|
||||
|
||||
if (!items[i].text.is_empty()) {
|
||||
int max_len = -1;
|
||||
|
||||
Vector2 size2 = items[i].text_buf->get_size();
|
||||
if (fixed_column_width) {
|
||||
max_len = fixed_column_width;
|
||||
} else if (same_column_width) {
|
||||
max_len = items[i].rect_cache.size.x;
|
||||
} else {
|
||||
max_len = size2.x;
|
||||
}
|
||||
|
||||
Color txt_modulate;
|
||||
if (items[i].selected) {
|
||||
if (items[i].selected && hovered == i) {
|
||||
txt_modulate = theme_cache.font_hovered_selected_color;
|
||||
} else if (items[i].selected) {
|
||||
txt_modulate = theme_cache.font_selected_color;
|
||||
} else if (hovered == i) {
|
||||
txt_modulate = theme_cache.font_hovered_color;
|
||||
|
|
@ -1283,18 +1379,20 @@ void ItemList::_notification(int p_what) {
|
|||
text_ofs += base_ofs;
|
||||
text_ofs += items[i].rect_cache.position;
|
||||
|
||||
text_ofs.x += theme_cache.h_separation / 2;
|
||||
text_ofs.y += theme_cache.v_separation / 2;
|
||||
|
||||
if (rtl) {
|
||||
text_ofs.x = size.width - text_ofs.x - max_len;
|
||||
}
|
||||
text_ofs.y += MAX(theme_cache.v_separation, 0) / 2;
|
||||
|
||||
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
|
||||
float text_w = items[i].rect_cache.size.width - theme_cache.h_separation;
|
||||
float text_w = items[i].rect_cache.size.width;
|
||||
if (wraparound_items && items[i].rect_cache.size.width > width) {
|
||||
text_w -= items[i].rect_cache.size.width - width;
|
||||
}
|
||||
items.write[i].text_buf->set_width(text_w);
|
||||
|
||||
if (rtl) {
|
||||
text_ofs.x = size.width - text_ofs.x - text_w;
|
||||
}
|
||||
|
||||
if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) {
|
||||
items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color);
|
||||
}
|
||||
|
|
@ -1307,21 +1405,27 @@ void ItemList::_notification(int p_what) {
|
|||
|
||||
if (icon_mode == ICON_MODE_TOP) {
|
||||
text_ofs.x += (items[i].rect_cache.size.width - size2.x) / 2;
|
||||
text_ofs.x += theme_cache.h_separation / 2;
|
||||
text_ofs.y += theme_cache.v_separation / 2;
|
||||
text_ofs.x += MAX(theme_cache.h_separation, 0) / 2;
|
||||
text_ofs.y += MAX(theme_cache.v_separation, 0) / 2;
|
||||
} else {
|
||||
text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2;
|
||||
text_ofs.x += theme_cache.h_separation / 2;
|
||||
text_ofs.x += MAX(theme_cache.h_separation, 0) / 2;
|
||||
}
|
||||
|
||||
text_ofs += base_ofs;
|
||||
text_ofs += items[i].rect_cache.position;
|
||||
|
||||
float text_w = width - text_ofs.x - theme_cache.h_separation;
|
||||
float text_w = items[i].rect_cache.size.width - icon_size.x - MAX(theme_cache.h_separation, 0);
|
||||
if (wraparound_items && items[i].rect_cache.size.width > width) {
|
||||
text_w -= items[i].rect_cache.size.width - width;
|
||||
}
|
||||
items.write[i].text_buf->set_width(text_w);
|
||||
|
||||
if (rtl) {
|
||||
text_ofs.x = size.width - width;
|
||||
text_ofs.x = size.width - items[i].rect_cache.size.width + icon_size.x - text_ofs.x + MAX(theme_cache.h_separation, 0);
|
||||
if (wraparound_items) {
|
||||
text_ofs.x += MAX(items[i].rect_cache.size.width - width, 0);
|
||||
}
|
||||
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
|
||||
} else {
|
||||
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
|
|
@ -1331,23 +1435,43 @@ void ItemList::_notification(int p_what) {
|
|||
items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color);
|
||||
}
|
||||
|
||||
if (width - text_ofs.x > 0) {
|
||||
items[i].text_buf->draw(get_canvas_item(), text_ofs, txt_modulate);
|
||||
if (fixed_column_width > 0) {
|
||||
if (items[i].rect_cache.size.width - icon_size.x - MAX(theme_cache.h_separation, 0) > 0) {
|
||||
items[i].text_buf->draw(get_canvas_item(), text_ofs, txt_modulate);
|
||||
}
|
||||
} else {
|
||||
if (wraparound_items) {
|
||||
if (width - icon_size.x - MAX(theme_cache.h_separation, 0) - int(scroll_bar_h->get_value()) > 0) {
|
||||
items[i].text_buf->draw(get_canvas_item(), text_ofs, txt_modulate);
|
||||
}
|
||||
} else {
|
||||
items[i].text_buf->draw(get_canvas_item(), text_ofs, txt_modulate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (select_mode == SELECT_MULTI && i == current) {
|
||||
Rect2 r = rcache;
|
||||
r.position += base_ofs;
|
||||
|
||||
if (rtl) {
|
||||
r.position.x = size.width - r.position.x - r.size.x;
|
||||
}
|
||||
|
||||
draw_style_box(cursor, r);
|
||||
if (i == current && (select_mode == SELECT_MULTI || select_mode == SELECT_TOGGLE)) {
|
||||
cursor_rcache = rcache;
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor_rcache.size != Size2()) { // Draw cursor last, so border isn't cut off.
|
||||
cursor_rcache.position += base_ofs;
|
||||
|
||||
if (rtl) {
|
||||
cursor_rcache.position.x = size.width - cursor_rcache.position.x - cursor_rcache.size.x;
|
||||
}
|
||||
|
||||
draw_style_box(cursor, cursor_rcache);
|
||||
}
|
||||
|
||||
if (has_focus()) {
|
||||
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true);
|
||||
size.x -= (scroll_bar_h->get_max() - scroll_bar_h->get_page());
|
||||
draw_style_box(theme_cache.focus_style, Rect2(Point2(), size));
|
||||
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1357,7 +1481,7 @@ void ItemList::force_update_list_size() {
|
|||
return;
|
||||
}
|
||||
|
||||
int scroll_bar_minwidth = scroll_bar->get_minimum_size().x;
|
||||
int scroll_bar_v_minwidth = scroll_bar_v->get_minimum_size().x;
|
||||
Size2 size = get_size();
|
||||
float max_column_width = 0.0;
|
||||
|
||||
|
|
@ -1384,8 +1508,6 @@ void ItemList::force_update_list_size() {
|
|||
int max_width = -1;
|
||||
if (fixed_column_width) {
|
||||
max_width = fixed_column_width;
|
||||
} else if (same_column_width) {
|
||||
max_width = items[i].rect_cache.size.x;
|
||||
}
|
||||
items.write[i].text_buf->set_width(max_width);
|
||||
Size2 s = items[i].text_buf->get_size();
|
||||
|
|
@ -1410,14 +1532,17 @@ void ItemList::force_update_list_size() {
|
|||
max_column_width = MAX(max_column_width, minsize.x);
|
||||
|
||||
// Elements need to adapt to the selected size.
|
||||
minsize.y += theme_cache.v_separation;
|
||||
minsize.x += theme_cache.h_separation;
|
||||
minsize.y += MAX(theme_cache.v_separation, 0);
|
||||
minsize.x += MAX(theme_cache.h_separation, 0);
|
||||
|
||||
items.write[i].rect_cache.size = minsize;
|
||||
items.write[i].min_rect_cache.size = minsize;
|
||||
}
|
||||
|
||||
int fit_size = size.x - theme_cache.panel_style->get_minimum_size().width - scroll_bar_minwidth;
|
||||
int fit_size = size.x - theme_cache.panel_style->get_minimum_size().width;
|
||||
if (!wraparound_items) {
|
||||
fit_size += (scroll_bar_h->get_max() - scroll_bar_h->get_page());
|
||||
}
|
||||
|
||||
//2-attempt best fit
|
||||
current_columns = 0x7FFFFFFF;
|
||||
|
|
@ -1430,12 +1555,13 @@ void ItemList::force_update_list_size() {
|
|||
bool all_fit = true;
|
||||
Vector2 ofs;
|
||||
int col = 0;
|
||||
int max_w = 0;
|
||||
int max_h = 0;
|
||||
|
||||
separators.clear();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) {
|
||||
if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size && !auto_width && wraparound_items) {
|
||||
// Went past.
|
||||
current_columns = MAX(col, 1);
|
||||
all_fit = false;
|
||||
|
|
@ -1443,12 +1569,13 @@ void ItemList::force_update_list_size() {
|
|||
}
|
||||
|
||||
if (same_column_width) {
|
||||
items.write[i].rect_cache.size.x = max_column_width + theme_cache.h_separation;
|
||||
items.write[i].rect_cache.size.x = max_column_width + MAX(theme_cache.h_separation, 0);
|
||||
}
|
||||
items.write[i].rect_cache.position = ofs;
|
||||
|
||||
max_h = MAX(max_h, items[i].rect_cache.size.y);
|
||||
ofs.x += items[i].rect_cache.size.x;
|
||||
max_w = MAX(max_w, ofs.x);
|
||||
|
||||
items.write[i].column = col;
|
||||
col++;
|
||||
|
|
@ -1468,28 +1595,58 @@ void ItemList::force_update_list_size() {
|
|||
}
|
||||
}
|
||||
|
||||
float scroll_bar_v_page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height);
|
||||
float scroll_bar_v_max = MAX(scroll_bar_v_page, ofs.y + max_h);
|
||||
float scroll_bar_h_page = MAX(0, size.width - theme_cache.panel_style->get_minimum_size().width);
|
||||
float scroll_bar_h_max = 0;
|
||||
if (!wraparound_items) {
|
||||
scroll_bar_h_max = MAX(scroll_bar_h_page, max_w);
|
||||
}
|
||||
|
||||
if (scroll_bar_v_page >= scroll_bar_v_max || is_layout_rtl()) {
|
||||
fit_size -= scroll_bar_v_minwidth;
|
||||
}
|
||||
|
||||
if (all_fit) {
|
||||
for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) {
|
||||
items.write[j].rect_cache.size.y = max_h;
|
||||
}
|
||||
|
||||
float page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height);
|
||||
float max = MAX(page, ofs.y + max_h);
|
||||
if (auto_height) {
|
||||
auto_height_value = ofs.y + max_h + theme_cache.panel_style->get_minimum_size().height;
|
||||
}
|
||||
scroll_bar->set_max(max);
|
||||
scroll_bar->set_page(page);
|
||||
if (max <= page) {
|
||||
scroll_bar->set_value(0);
|
||||
scroll_bar->hide();
|
||||
if (auto_width) {
|
||||
auto_width_value = max_w + theme_cache.panel_style->get_minimum_size().width;
|
||||
}
|
||||
scroll_bar_v->set_max(scroll_bar_v_max);
|
||||
scroll_bar_v->set_page(scroll_bar_v_page);
|
||||
if (scroll_bar_v_max <= scroll_bar_v_page) {
|
||||
scroll_bar_v->set_value(0);
|
||||
scroll_bar_v->hide();
|
||||
} else {
|
||||
scroll_bar->show();
|
||||
auto_width_value += scroll_bar_v_minwidth;
|
||||
scroll_bar_v->show();
|
||||
|
||||
if (do_autoscroll_to_bottom) {
|
||||
scroll_bar->set_value(max);
|
||||
scroll_bar_v->set_value(scroll_bar_v_max);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_layout_rtl() && !wraparound_items) {
|
||||
scroll_bar_h->set_max(scroll_bar_h_page);
|
||||
scroll_bar_h->set_min(-(scroll_bar_h_max - scroll_bar_h_page));
|
||||
} else {
|
||||
scroll_bar_h->set_max(scroll_bar_h_max);
|
||||
scroll_bar_h->set_min(0);
|
||||
}
|
||||
scroll_bar_h->set_page(scroll_bar_h_page);
|
||||
if (scroll_bar_h_max <= scroll_bar_h_page) {
|
||||
scroll_bar_h->set_value(0);
|
||||
scroll_bar_h->hide();
|
||||
} else {
|
||||
auto_height_value += scroll_bar_h->get_minimum_size().y;
|
||||
scroll_bar_h->show();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1509,13 +1666,31 @@ void ItemList::_mouse_exited() {
|
|||
}
|
||||
}
|
||||
|
||||
String ItemList::_atr(int p_idx, const String &p_text) const {
|
||||
ERR_FAIL_INDEX_V(p_idx, items.size(), atr(p_text));
|
||||
switch (items[p_idx].auto_translate_mode) {
|
||||
case AUTO_TRANSLATE_MODE_INHERIT: {
|
||||
return atr(p_text);
|
||||
} break;
|
||||
case AUTO_TRANSLATE_MODE_ALWAYS: {
|
||||
return tr(p_text);
|
||||
} break;
|
||||
case AUTO_TRANSLATE_MODE_DISABLED: {
|
||||
return p_text;
|
||||
} break;
|
||||
}
|
||||
|
||||
ERR_FAIL_V_MSG(atr(p_text), "Unexpected auto translate mode: " + itos(items[p_idx].auto_translate_mode));
|
||||
}
|
||||
|
||||
int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const {
|
||||
Vector2 pos = p_pos;
|
||||
pos -= theme_cache.panel_style->get_offset();
|
||||
pos.y += scroll_bar->get_value();
|
||||
pos.y += scroll_bar_v->get_value();
|
||||
pos.x += scroll_bar_h->get_value();
|
||||
|
||||
if (is_layout_rtl()) {
|
||||
pos.x = get_size().width - pos.x;
|
||||
pos.x = get_size().width - pos.x - scroll_bar_h->get_value() - theme_cache.panel_style->get_margin(SIDE_LEFT) - theme_cache.panel_style->get_margin(SIDE_RIGHT);
|
||||
}
|
||||
|
||||
int closest = -1;
|
||||
|
|
@ -1524,8 +1699,16 @@ int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const {
|
|||
for (int i = 0; i < items.size(); i++) {
|
||||
Rect2 rc = items[i].rect_cache;
|
||||
|
||||
if (i % current_columns == current_columns - 1) {
|
||||
rc.size.width = get_size().width - rc.position.x; // Make sure you can still select the last item when clicking past the column.
|
||||
if (i % current_columns == current_columns - 1) { // Make sure you can still select the last item when clicking past the column.
|
||||
if (is_layout_rtl()) {
|
||||
rc.size.width = get_size().width - scroll_bar_h->get_value() + rc.position.x;
|
||||
} else {
|
||||
rc.size.width = get_size().width + scroll_bar_h->get_value() - rc.position.x;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc.size.x < 0) {
|
||||
continue; // Skip negative item sizes, because they are off screen.
|
||||
}
|
||||
|
||||
if (rc.has_point(pos)) {
|
||||
|
|
@ -1550,7 +1733,7 @@ bool ItemList::is_pos_at_end_of_items(const Point2 &p_pos) const {
|
|||
|
||||
Vector2 pos = p_pos;
|
||||
pos -= theme_cache.panel_style->get_offset();
|
||||
pos.y += scroll_bar->get_value();
|
||||
pos.y += scroll_bar_v->get_value();
|
||||
|
||||
if (is_layout_rtl()) {
|
||||
pos.x = get_size().width - pos.x;
|
||||
|
|
@ -1667,16 +1850,35 @@ bool ItemList::is_anything_selected() {
|
|||
}
|
||||
|
||||
Size2 ItemList::get_minimum_size() const {
|
||||
if (auto_height) {
|
||||
return Size2(0, auto_height_value);
|
||||
Size2 min_size;
|
||||
if (auto_width) {
|
||||
min_size.x = auto_width_value;
|
||||
}
|
||||
return Size2();
|
||||
|
||||
if (auto_height) {
|
||||
min_size.y = auto_height_value;
|
||||
}
|
||||
return min_size;
|
||||
}
|
||||
|
||||
void ItemList::set_autoscroll_to_bottom(const bool p_enable) {
|
||||
do_autoscroll_to_bottom = p_enable;
|
||||
}
|
||||
|
||||
void ItemList::set_auto_width(bool p_enable) {
|
||||
if (auto_width == p_enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto_width = p_enable;
|
||||
shape_changed = true;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
bool ItemList::has_auto_width() const {
|
||||
return auto_width;
|
||||
}
|
||||
|
||||
void ItemList::set_auto_height(bool p_enable) {
|
||||
if (auto_height == p_enable) {
|
||||
return;
|
||||
|
|
@ -1706,6 +1908,20 @@ TextServer::OverrunBehavior ItemList::get_text_overrun_behavior() const {
|
|||
return text_overrun_behavior;
|
||||
}
|
||||
|
||||
void ItemList::set_wraparound_items(bool p_enable) {
|
||||
if (wraparound_items == p_enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
wraparound_items = p_enable;
|
||||
shape_changed = true;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
bool ItemList::has_wraparound_items() const {
|
||||
return wraparound_items;
|
||||
}
|
||||
|
||||
bool ItemList::_set(const StringName &p_name, const Variant &p_value) {
|
||||
if (property_helper.property_set_value(p_name, p_value)) {
|
||||
return true;
|
||||
|
|
@ -1748,6 +1964,9 @@ void ItemList::_bind_methods() {
|
|||
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);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_item_auto_translate_mode", "idx", "mode"), &ItemList::set_item_auto_translate_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_item_auto_translate_mode", "idx"), &ItemList::get_item_auto_translate_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_item_icon_transposed", "idx", "transposed"), &ItemList::set_item_icon_transposed);
|
||||
ClassDB::bind_method(D_METHOD("is_item_icon_transposed", "idx"), &ItemList::is_item_icon_transposed);
|
||||
|
||||
|
|
@ -1829,6 +2048,9 @@ void ItemList::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_allow_search", "allow"), &ItemList::set_allow_search);
|
||||
ClassDB::bind_method(D_METHOD("get_allow_search"), &ItemList::get_allow_search);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_auto_width", "enable"), &ItemList::set_auto_width);
|
||||
ClassDB::bind_method(D_METHOD("has_auto_width"), &ItemList::has_auto_width);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_auto_height", "enable"), &ItemList::set_auto_height);
|
||||
ClassDB::bind_method(D_METHOD("has_auto_height"), &ItemList::has_auto_height);
|
||||
|
||||
|
|
@ -1839,19 +2061,25 @@ void ItemList::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("ensure_current_is_visible"), &ItemList::ensure_current_is_visible);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &ItemList::get_v_scroll_bar);
|
||||
ClassDB::bind_method(D_METHOD("get_h_scroll_bar"), &ItemList::get_h_scroll_bar);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &ItemList::set_text_overrun_behavior);
|
||||
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &ItemList::get_text_overrun_behavior);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_wraparound_items", "enable"), &ItemList::set_wraparound_items);
|
||||
ClassDB::bind_method(D_METHOD("has_wraparound_items"), &ItemList::has_wraparound_items);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("force_update_list_size"), &ItemList::force_update_list_size);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Multi"), "set_select_mode", "get_select_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Multi,Toggle"), "set_select_mode", "get_select_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_text_lines", PROPERTY_HINT_RANGE, "1,10,1,or_greater"), "set_max_text_lines", "get_max_text_lines");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_width"), "set_auto_width", "has_auto_width");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wraparound_items"), "set_wraparound_items", "has_wraparound_items");
|
||||
ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "item_");
|
||||
ADD_GROUP("Columns", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_columns", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), "set_max_columns", "get_max_columns");
|
||||
|
|
@ -1867,6 +2095,7 @@ void ItemList::_bind_methods() {
|
|||
|
||||
BIND_ENUM_CONSTANT(SELECT_SINGLE);
|
||||
BIND_ENUM_CONSTANT(SELECT_MULTI);
|
||||
BIND_ENUM_CONSTANT(SELECT_TOGGLE);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index")));
|
||||
ADD_SIGNAL(MethodInfo("empty_clicked", PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index")));
|
||||
|
|
@ -1884,6 +2113,7 @@ void ItemList::_bind_methods() {
|
|||
BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, ItemList, font_size);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, ItemList, font_color);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, ItemList, font_hovered_color);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, ItemList, font_hovered_selected_color);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, ItemList, font_selected_color);
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, ItemList, font_outline_size, "outline_size");
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, ItemList, font_outline_color);
|
||||
|
|
@ -1891,6 +2121,8 @@ void ItemList::_bind_methods() {
|
|||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, ItemList, line_separation);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, ItemList, icon_margin);
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ItemList, hovered_style, "hovered");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ItemList, hovered_selected_style, "hovered_selected");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ItemList, hovered_selected_focus_style, "hovered_selected_focus");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ItemList, selected_style, "selected");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ItemList, selected_focus_style, "selected_focus");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ItemList, cursor_style, "cursor_unfocused");
|
||||
|
|
@ -1909,9 +2141,13 @@ void ItemList::_bind_methods() {
|
|||
}
|
||||
|
||||
ItemList::ItemList() {
|
||||
scroll_bar = memnew(VScrollBar);
|
||||
add_child(scroll_bar, false, INTERNAL_MODE_FRONT);
|
||||
scroll_bar->connect(SceneStringName(value_changed), callable_mp(this, &ItemList::_scroll_changed));
|
||||
scroll_bar_v = memnew(VScrollBar);
|
||||
add_child(scroll_bar_v, false, INTERNAL_MODE_FRONT);
|
||||
scroll_bar_v->connect(SceneStringName(value_changed), callable_mp(this, &ItemList::_scroll_changed));
|
||||
|
||||
scroll_bar_h = memnew(HScrollBar);
|
||||
add_child(scroll_bar_h, false, INTERNAL_MODE_FRONT);
|
||||
scroll_bar_h->connect(SceneStringName(value_changed), callable_mp(this, &ItemList::_scroll_changed));
|
||||
|
||||
connect(SceneStringName(mouse_exited), callable_mp(this, &ItemList::_mouse_exited));
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ public:
|
|||
|
||||
enum SelectMode {
|
||||
SELECT_SINGLE,
|
||||
SELECT_MULTI
|
||||
SELECT_MULTI,
|
||||
SELECT_TOGGLE,
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
@ -62,6 +63,7 @@ private:
|
|||
Ref<TextParagraph> text_buf;
|
||||
String language;
|
||||
TextDirection text_direction = TEXT_DIRECTION_AUTO;
|
||||
AutoTranslateMode auto_translate_mode = AUTO_TRANSLATE_MODE_INHERIT;
|
||||
|
||||
bool selectable = true;
|
||||
bool selected = false;
|
||||
|
|
@ -99,15 +101,21 @@ private:
|
|||
bool same_column_width = false;
|
||||
bool allow_search = true;
|
||||
|
||||
bool auto_width = false;
|
||||
float auto_width_value = 0.0;
|
||||
|
||||
bool auto_height = false;
|
||||
float auto_height_value = 0.0;
|
||||
|
||||
bool wraparound_items = true;
|
||||
|
||||
Vector<Item> items;
|
||||
Vector<int> separators;
|
||||
|
||||
SelectMode select_mode = SELECT_SINGLE;
|
||||
IconMode icon_mode = ICON_MODE_LEFT;
|
||||
VScrollBar *scroll_bar = nullptr;
|
||||
VScrollBar *scroll_bar_v = nullptr;
|
||||
HScrollBar *scroll_bar_h = nullptr;
|
||||
TextServer::OverrunBehavior text_overrun_behavior = TextServer::OVERRUN_TRIM_ELLIPSIS;
|
||||
|
||||
uint64_t search_time_msec = 0;
|
||||
|
|
@ -141,6 +149,7 @@ private:
|
|||
int font_size = 0;
|
||||
Color font_color;
|
||||
Color font_hovered_color;
|
||||
Color font_hovered_selected_color;
|
||||
Color font_selected_color;
|
||||
int font_outline_size = 0;
|
||||
Color font_outline_color;
|
||||
|
|
@ -148,6 +157,8 @@ private:
|
|||
int line_separation = 0;
|
||||
int icon_margin = 0;
|
||||
Ref<StyleBox> hovered_style;
|
||||
Ref<StyleBox> hovered_selected_style;
|
||||
Ref<StyleBox> hovered_selected_focus_style;
|
||||
Ref<StyleBox> selected_style;
|
||||
Ref<StyleBox> selected_focus_style;
|
||||
Ref<StyleBox> cursor_style;
|
||||
|
|
@ -159,6 +170,8 @@ private:
|
|||
void _shape_text(int p_idx);
|
||||
void _mouse_exited();
|
||||
|
||||
String _atr(int p_idx, const String &p_text) const;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
|
|
@ -183,6 +196,9 @@ public:
|
|||
void set_item_language(int p_idx, const String &p_language);
|
||||
String get_item_language(int p_idx) const;
|
||||
|
||||
void set_item_auto_translate_mode(int p_idx, AutoTranslateMode p_mode);
|
||||
AutoTranslateMode get_item_auto_translate_mode(int p_idx) const;
|
||||
|
||||
void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon);
|
||||
Ref<Texture2D> get_item_icon(int p_idx) const;
|
||||
|
||||
|
|
@ -285,16 +301,23 @@ public:
|
|||
void set_icon_scale(real_t p_scale);
|
||||
real_t get_icon_scale() const;
|
||||
|
||||
void set_auto_width(bool p_enable);
|
||||
bool has_auto_width() const;
|
||||
|
||||
void set_auto_height(bool p_enable);
|
||||
bool has_auto_height() const;
|
||||
|
||||
void set_wraparound_items(bool p_enable);
|
||||
bool has_wraparound_items() const;
|
||||
|
||||
Size2 get_minimum_size() const override;
|
||||
|
||||
void set_autoscroll_to_bottom(const bool p_enable);
|
||||
|
||||
void force_update_list_size();
|
||||
|
||||
VScrollBar *get_v_scroll_bar() { return scroll_bar; }
|
||||
VScrollBar *get_v_scroll_bar() { return scroll_bar_v; }
|
||||
HScrollBar *get_h_scroll_bar() { return scroll_bar_h; }
|
||||
|
||||
ItemList();
|
||||
~ItemList();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -54,14 +54,24 @@ private:
|
|||
bool clip = false;
|
||||
String el_char = U"…";
|
||||
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
|
||||
Size2 minsize;
|
||||
mutable Size2 minsize;
|
||||
bool uppercase = false;
|
||||
|
||||
bool lines_dirty = true;
|
||||
bool dirty = true;
|
||||
bool font_dirty = true;
|
||||
RID text_rid;
|
||||
Vector<RID> lines_rid;
|
||||
struct Paragraph {
|
||||
bool lines_dirty = true;
|
||||
bool dirty = true;
|
||||
int start = 0;
|
||||
|
||||
String text;
|
||||
RID text_rid;
|
||||
Vector<RID> lines_rid;
|
||||
};
|
||||
mutable bool dirty = true;
|
||||
mutable bool font_dirty = true;
|
||||
mutable bool text_dirty = true;
|
||||
mutable Vector<Paragraph> paragraphs;
|
||||
mutable int total_line_count = 0;
|
||||
String paragraph_separator = "\\n";
|
||||
|
||||
String language;
|
||||
TextDirection text_direction = TEXT_DIRECTION_AUTO;
|
||||
|
|
@ -83,6 +93,7 @@ private:
|
|||
|
||||
int font_size = 0;
|
||||
int line_spacing = 0;
|
||||
int paragraph_spacing = 0;
|
||||
Color font_color;
|
||||
Color font_shadow_color;
|
||||
Point2 font_shadow_offset;
|
||||
|
|
@ -91,11 +102,17 @@ private:
|
|||
int font_shadow_outline_size;
|
||||
} theme_cache;
|
||||
|
||||
void _update_visible();
|
||||
void _shape();
|
||||
Rect2 _get_line_rect(int p_para, int p_line) const;
|
||||
void _ensure_shaped() const;
|
||||
void _update_visible() const;
|
||||
void _shape() const;
|
||||
void _invalidate();
|
||||
|
||||
protected:
|
||||
RID get_line_rid(int p_line) const;
|
||||
Rect2 get_line_rect(int p_line) const;
|
||||
int get_layout_data(Vector2 &r_offset, int &r_last_line, int &r_line_spacing) const;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
|
@ -124,6 +141,9 @@ public:
|
|||
void set_language(const String &p_language);
|
||||
String get_language() const;
|
||||
|
||||
void set_paragraph_separator(const String &p_paragraph_separator);
|
||||
String get_paragraph_separator() const;
|
||||
|
||||
void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
|
||||
TextServer::StructuredTextParser get_structured_text_bidi_override() const;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -69,6 +69,7 @@ public:
|
|||
MENU_INSERT_ZWNJ,
|
||||
MENU_INSERT_WJ,
|
||||
MENU_INSERT_SHY,
|
||||
MENU_EMOJI_AND_SYMBOL,
|
||||
MENU_MAX
|
||||
};
|
||||
|
||||
|
|
@ -86,11 +87,14 @@ public:
|
|||
private:
|
||||
HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
|
||||
|
||||
bool editing = false;
|
||||
bool keep_editing_on_text_submit = false;
|
||||
bool editable = false;
|
||||
bool pass = false;
|
||||
bool text_changed_dirty = false;
|
||||
|
||||
bool alt_start = false;
|
||||
bool alt_start_no_hold = false;
|
||||
uint32_t alt_code = 0;
|
||||
|
||||
String undo_text;
|
||||
|
|
@ -109,6 +113,7 @@ private:
|
|||
bool drag_and_drop_selection_enabled = true;
|
||||
|
||||
bool context_menu_enabled = true;
|
||||
bool emoji_menu_enabled = true;
|
||||
PopupMenu *menu = nullptr;
|
||||
PopupMenu *menu_dir = nullptr;
|
||||
PopupMenu *menu_ctl = nullptr;
|
||||
|
|
@ -205,6 +210,9 @@ private:
|
|||
float base_scale = 1.0;
|
||||
} theme_cache;
|
||||
|
||||
void _close_ime_window();
|
||||
void _update_ime_window_position();
|
||||
|
||||
void _clear_undo_stack();
|
||||
void _clear_redo();
|
||||
void _create_undo_state();
|
||||
|
|
@ -243,6 +251,7 @@ private:
|
|||
void _move_caret_end(bool p_select);
|
||||
void _backspace(bool p_word = false, bool p_all_to_left = false);
|
||||
void _delete(bool p_word = false, bool p_all_to_right = false);
|
||||
void _texture_changed();
|
||||
|
||||
protected:
|
||||
bool _is_over_clear_button(const Point2 &p_pos) const;
|
||||
|
|
@ -257,6 +266,16 @@ protected:
|
|||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
public:
|
||||
void edit();
|
||||
void unedit();
|
||||
bool is_editing() const;
|
||||
void set_keep_editing_on_text_submit(bool p_enabled);
|
||||
bool is_editing_kept_on_text_submit() const;
|
||||
|
||||
bool has_ime_text() const;
|
||||
void cancel_ime();
|
||||
void apply_ime();
|
||||
|
||||
void set_horizontal_alignment(HorizontalAlignment p_alignment);
|
||||
HorizontalAlignment get_horizontal_alignment() const;
|
||||
|
||||
|
|
@ -272,6 +291,11 @@ public:
|
|||
PopupMenu *get_menu() const;
|
||||
bool is_menu_visible() const;
|
||||
|
||||
void show_emoji_and_symbol_picker();
|
||||
|
||||
void set_emoji_menu_enabled(bool p_enabled);
|
||||
bool is_emoji_menu_enabled() const;
|
||||
|
||||
void select(int p_from = 0, int p_to = -1);
|
||||
void select_all();
|
||||
void selection_delete();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "link_button.h"
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
void LinkButton::_shape() {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Size2 MarginContainer::get_minimum_size() const {
|
|||
Size2 max;
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "menu_bar.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
|
|
@ -121,8 +120,9 @@ void MenuBar::_open_popup(int p_index, bool p_focus_item) {
|
|||
}
|
||||
|
||||
Rect2 item_rect = _get_menu_item_rect(p_index);
|
||||
Point2 screen_pos = get_screen_position() + item_rect.position * get_viewport()->get_canvas_transform().get_scale();
|
||||
Size2 screen_size = item_rect.size * get_viewport()->get_canvas_transform().get_scale();
|
||||
Size2 canvas_scale = get_canvas_transform().get_scale();
|
||||
Point2 screen_pos = get_screen_position() + item_rect.position * canvas_scale;
|
||||
Size2 screen_size = item_rect.size * canvas_scale;
|
||||
|
||||
active_menu = p_index;
|
||||
|
||||
|
|
@ -217,15 +217,18 @@ void MenuBar::bind_global_menu() {
|
|||
int global_start_idx = -1;
|
||||
int count = nmenu->get_item_count(main_menu);
|
||||
String prev_tag;
|
||||
for (int i = 0; i < count; i++) {
|
||||
String tag = nmenu->get_item_tag(main_menu, i).operator String().get_slice("#", 1);
|
||||
if (!tag.is_empty() && tag != prev_tag) {
|
||||
if (i >= start_index) {
|
||||
global_start_idx = i;
|
||||
break;
|
||||
if (start_index >= 0) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
String tag = nmenu->get_item_tag(main_menu, i).operator String().get_slice("#", 1);
|
||||
if (!tag.is_empty() && tag != prev_tag) {
|
||||
MenuBar *mb = Object::cast_to<MenuBar>(ObjectDB::get_instance(ObjectID(static_cast<uint64_t>(tag.to_int()))));
|
||||
if (mb && mb->get_start_index() >= start_index) {
|
||||
global_start_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
prev_tag = tag;
|
||||
}
|
||||
prev_tag = tag;
|
||||
}
|
||||
if (global_start_idx == -1) {
|
||||
global_start_idx = count;
|
||||
|
|
@ -503,9 +506,11 @@ void MenuBar::_refresh_menu_names() {
|
|||
|
||||
Vector<PopupMenu *> popups = _get_popups();
|
||||
for (int i = 0; i < popups.size(); i++) {
|
||||
if (!popups[i]->has_meta("_menu_name") && String(popups[i]->get_name()) != get_menu_title(i)) {
|
||||
menu_cache.write[i].name = popups[i]->get_name();
|
||||
String menu_name = popups[i]->get_title().is_empty() ? String(popups[i]->get_name()) : popups[i]->get_title();
|
||||
if (!popups[i]->has_meta("_menu_name") && menu_name != get_menu_title(i)) {
|
||||
menu_cache.write[i].name = menu_name;
|
||||
shape(menu_cache.write[i]);
|
||||
queue_redraw();
|
||||
if (is_global && menu_cache[i].submenu_rid.is_valid()) {
|
||||
int item_idx = nmenu->find_item_index_with_submenu(main_menu, menu_cache[i].submenu_rid);
|
||||
if (item_idx >= 0) {
|
||||
|
|
@ -542,6 +547,24 @@ int MenuBar::get_menu_idx_from_control(PopupMenu *p_child) const {
|
|||
return -1;
|
||||
}
|
||||
|
||||
void MenuBar::_popup_changed(ObjectID p_menu) {
|
||||
PopupMenu *pm = Object::cast_to<PopupMenu>(ObjectDB::get_instance(p_menu));
|
||||
if (!pm) {
|
||||
return;
|
||||
}
|
||||
|
||||
int idx = get_menu_idx_from_control(pm);
|
||||
|
||||
String menu_name = pm->get_title().is_empty() ? String(pm->get_name()) : pm->get_title();
|
||||
menu_name = String(pm->get_meta("_menu_name", menu_name));
|
||||
|
||||
menu_cache.write[idx].name = menu_name;
|
||||
shape(menu_cache.write[idx]);
|
||||
|
||||
update_minimum_size();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void MenuBar::add_child_notify(Node *p_child) {
|
||||
Control::add_child_notify(p_child);
|
||||
|
||||
|
|
@ -549,9 +572,12 @@ void MenuBar::add_child_notify(Node *p_child) {
|
|||
if (!pm) {
|
||||
return;
|
||||
}
|
||||
Menu menu = Menu(p_child->get_name());
|
||||
String menu_name = pm->get_title().is_empty() ? String(pm->get_name()) : pm->get_title();
|
||||
Menu menu = Menu(menu_name);
|
||||
shape(menu);
|
||||
|
||||
pm->connect("title_changed", callable_mp(this, &MenuBar::_popup_changed).bind(pm->get_instance_id()), CONNECT_REFERENCE_COUNTED);
|
||||
|
||||
menu_cache.push_back(menu);
|
||||
p_child->connect("renamed", callable_mp(this, &MenuBar::_refresh_menu_names));
|
||||
p_child->connect("about_to_popup", callable_mp(this, &MenuBar::_popup_visibility_changed).bind(true));
|
||||
|
|
@ -579,7 +605,8 @@ void MenuBar::move_child_notify(Node *p_child) {
|
|||
}
|
||||
|
||||
int old_idx = -1;
|
||||
String menu_name = String(pm->get_meta("_menu_name", pm->get_name()));
|
||||
String menu_name = pm->get_title().is_empty() ? String(pm->get_name()) : pm->get_title();
|
||||
menu_name = String(pm->get_meta("_menu_name", menu_name));
|
||||
// Find the previous menu index of the control.
|
||||
for (int i = 0; i < get_menu_count(); i++) {
|
||||
if (get_menu_title(i) == menu_name) {
|
||||
|
|
@ -635,6 +662,7 @@ void MenuBar::remove_child_notify(Node *p_child) {
|
|||
}
|
||||
}
|
||||
|
||||
pm->disconnect("title_changed", callable_mp(this, &MenuBar::_popup_changed));
|
||||
menu_cache.remove_at(idx);
|
||||
|
||||
p_child->remove_meta("_menu_name");
|
||||
|
|
@ -679,10 +707,7 @@ void MenuBar::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_menu_hidden", "menu", "hidden"), &MenuBar::set_menu_hidden);
|
||||
ClassDB::bind_method(D_METHOD("is_menu_hidden", "menu"), &MenuBar::is_menu_hidden);
|
||||
|
||||
// TODO: Properly handle popups when advanced GUI is disabled.
|
||||
#ifndef ADVANCED_GUI_DISABLED
|
||||
ClassDB::bind_method(D_METHOD("get_menu_popup", "menu"), &MenuBar::get_menu_popup);
|
||||
#endif // ADVANCED_GUI_DISABLED
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "start_index"), "set_start_index", "get_start_index");
|
||||
|
|
@ -825,7 +850,8 @@ int MenuBar::get_menu_count() const {
|
|||
void MenuBar::set_menu_title(int p_menu, const String &p_title) {
|
||||
ERR_FAIL_INDEX(p_menu, menu_cache.size());
|
||||
PopupMenu *pm = get_menu_popup(p_menu);
|
||||
if (p_title == pm->get_name()) {
|
||||
String menu_name = pm->get_title().is_empty() ? String(pm->get_name()) : pm->get_title();
|
||||
if (p_title == menu_name) {
|
||||
pm->remove_meta("_menu_name");
|
||||
} else {
|
||||
pm->set_meta("_menu_name", p_title);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#ifndef MENU_BAR_H
|
||||
#define MENU_BAR_H
|
||||
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
||||
class MenuBar : public Control {
|
||||
|
|
@ -135,6 +134,8 @@ class MenuBar : public Control {
|
|||
return -1;
|
||||
}
|
||||
|
||||
void _popup_changed(ObjectID p_menu);
|
||||
|
||||
void bind_global_menu();
|
||||
void unbind_global_menu();
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "menu_button.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "scene/main/window.h"
|
||||
|
||||
void MenuButton::shortcut_input(const Ref<InputEvent> &p_event) {
|
||||
|
|
@ -138,7 +137,7 @@ void MenuButton::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
MenuButton *menu_btn_other = Object::cast_to<MenuButton>(get_viewport()->gui_find_control(get_viewport()->get_mouse_position()));
|
||||
MenuButton *menu_btn_other = Object::cast_to<MenuButton>(get_viewport()->gui_get_hovered_control());
|
||||
|
||||
if (menu_btn_other && menu_btn_other != this && menu_btn_other->is_switch_on_hover() && !menu_btn_other->is_disabled() &&
|
||||
(get_parent()->is_ancestor_of(menu_btn_other) || menu_btn_other->get_parent()->is_ancestor_of(popup))) {
|
||||
|
|
@ -173,10 +172,7 @@ bool MenuButton::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
}
|
||||
|
||||
void MenuButton::_bind_methods() {
|
||||
// TODO: Properly handle popups when advanced GUI is disabled.
|
||||
#ifndef ADVANCED_GUI_DISABLED
|
||||
ClassDB::bind_method(D_METHOD("get_popup"), &MenuButton::get_popup);
|
||||
#endif // ADVANCED_GUI_DISABLED
|
||||
ClassDB::bind_method(D_METHOD("show_popup"), &MenuButton::show_popup);
|
||||
ClassDB::bind_method(D_METHOD("set_switch_on_hover", "enable"), &MenuButton::set_switch_on_hover);
|
||||
ClassDB::bind_method(D_METHOD("is_switch_on_hover"), &MenuButton::is_switch_on_hover);
|
||||
|
|
@ -198,7 +194,7 @@ void MenuButton::_bind_methods() {
|
|||
base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::INT, "checkable", PROPERTY_HINT_ENUM, "No,As Checkbox,As Radio Button"), defaults.checkable_type);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::BOOL, "checked"), defaults.checked);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), defaults.id);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL), defaults.id);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator);
|
||||
PropertyListHelper::register_base_helper(&base_property_helper);
|
||||
|
|
@ -208,6 +204,14 @@ void MenuButton::set_disable_shortcuts(bool p_disabled) {
|
|||
disable_shortcuts = p_disabled;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
PackedStringArray MenuButton::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Button::get_configuration_warnings();
|
||||
warnings.append_array(popup->get_configuration_warnings());
|
||||
return warnings;
|
||||
}
|
||||
#endif
|
||||
|
||||
MenuButton::MenuButton(const String &p_text) :
|
||||
Button(p_text) {
|
||||
set_flat(true);
|
||||
|
|
|
|||
|
|
@ -71,6 +71,10 @@ public:
|
|||
void set_item_count(int p_count);
|
||||
int get_item_count() const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
#endif
|
||||
|
||||
MenuButton(const String &p_text = String());
|
||||
~MenuButton();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include "option_button.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
static const int NONE_SELECTED = -1;
|
||||
|
|
@ -178,7 +176,7 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) {
|
|||
}
|
||||
|
||||
void OptionButton::_focused(int p_which) {
|
||||
emit_signal(SNAME("item_focused"), p_which);
|
||||
emit_signal(SNAME("item_focused"), popup->get_item_index(p_which));
|
||||
}
|
||||
|
||||
void OptionButton::_selected(int p_which) {
|
||||
|
|
@ -225,7 +223,7 @@ void OptionButton::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
|
|||
popup->set_item_icon(p_idx, p_icon);
|
||||
|
||||
if (current == p_idx) {
|
||||
set_icon(p_icon);
|
||||
set_button_icon(p_icon);
|
||||
}
|
||||
_queue_update_size_cache();
|
||||
}
|
||||
|
|
@ -381,7 +379,7 @@ void OptionButton::_select(int p_which, bool p_emit) {
|
|||
|
||||
current = NONE_SELECTED;
|
||||
set_text("");
|
||||
set_icon(nullptr);
|
||||
set_button_icon(nullptr);
|
||||
} else {
|
||||
ERR_FAIL_INDEX(p_which, popup->get_item_count());
|
||||
|
||||
|
|
@ -391,7 +389,7 @@ void OptionButton::_select(int p_which, bool p_emit) {
|
|||
|
||||
current = p_which;
|
||||
set_text(popup->get_item_text(current));
|
||||
set_icon(popup->get_item_icon(current));
|
||||
set_button_icon(popup->get_item_icon(current));
|
||||
}
|
||||
|
||||
if (is_inside_tree() && p_emit) {
|
||||
|
|
@ -470,12 +468,6 @@ void OptionButton::show_popup() {
|
|||
return;
|
||||
}
|
||||
|
||||
Rect2 rect = get_screen_rect();
|
||||
rect.position.y += rect.size.height;
|
||||
rect.size.height = 0;
|
||||
popup->set_position(rect.position);
|
||||
popup->set_size(rect.size);
|
||||
|
||||
// If not triggered by the mouse, start the popup with the checked item (or the first enabled one) focused.
|
||||
if (current != NONE_SELECTED && !popup->is_item_disabled(current)) {
|
||||
if (!_was_pressed_by_mouse()) {
|
||||
|
|
@ -497,7 +489,10 @@ void OptionButton::show_popup() {
|
|||
}
|
||||
}
|
||||
|
||||
popup->popup();
|
||||
Rect2 rect = get_screen_rect();
|
||||
rect.position.y += rect.size.height;
|
||||
rect.size.height = 0;
|
||||
popup->popup(rect);
|
||||
}
|
||||
|
||||
void OptionButton::_validate_property(PropertyInfo &p_property) const {
|
||||
|
|
@ -574,7 +569,7 @@ void OptionButton::_bind_methods() {
|
|||
base_property_helper.set_array_length_getter(&OptionButton::get_item_count);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::STRING, "text"), defaults.text, &OptionButton::_dummy_setter, &OptionButton::get_item_text);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &OptionButton::_dummy_setter, &OptionButton::get_item_icon);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), defaults.id, &OptionButton::_dummy_setter, &OptionButton::get_item_id);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL), defaults.id, &OptionButton::_dummy_setter, &OptionButton::get_item_id);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &OptionButton::_dummy_setter, &OptionButton::is_item_disabled);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator, &OptionButton::_dummy_setter, &OptionButton::is_item_separator);
|
||||
PropertyListHelper::register_base_helper(&base_property_helper);
|
||||
|
|
@ -584,6 +579,14 @@ void OptionButton::set_disable_shortcuts(bool p_disabled) {
|
|||
disable_shortcuts = p_disabled;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
PackedStringArray OptionButton::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Button::get_configuration_warnings();
|
||||
warnings.append_array(popup->get_configuration_warnings());
|
||||
return warnings;
|
||||
}
|
||||
#endif
|
||||
|
||||
OptionButton::OptionButton(const String &p_text) :
|
||||
Button(p_text) {
|
||||
set_toggle_mode(true);
|
||||
|
|
|
|||
|
|
@ -143,6 +143,10 @@ public:
|
|||
|
||||
void set_disable_shortcuts(bool p_disabled);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
#endif
|
||||
|
||||
OptionButton(const String &p_text = String());
|
||||
~OptionButton();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
Size2 PanelContainer::get_minimum_size() const {
|
||||
Size2 ms;
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,11 @@
|
|||
|
||||
#include "popup.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "core/config/project_settings.h"
|
||||
#endif
|
||||
#include "scene/gui/panel.h"
|
||||
#include "scene/resources/style_box_flat.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
|
||||
|
|
@ -222,6 +224,45 @@ Popup::Popup() {
|
|||
Popup::~Popup() {
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
PackedStringArray PopupPanel::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Popup::get_configuration_warnings();
|
||||
|
||||
if (!DisplayServer::get_singleton()->is_window_transparency_available() && !GLOBAL_GET("display/window/subwindows/embed_subwindows")) {
|
||||
Ref<StyleBoxFlat> sb = theme_cache.panel_style;
|
||||
if (sb.is_valid() && (sb->get_shadow_size() > 0 || sb->get_corner_radius(CORNER_TOP_LEFT) > 0 || sb->get_corner_radius(CORNER_TOP_RIGHT) > 0 || sb->get_corner_radius(CORNER_BOTTOM_LEFT) > 0 || sb->get_corner_radius(CORNER_BOTTOM_RIGHT) > 0)) {
|
||||
warnings.push_back(RTR("The current theme style has shadows and/or rounded corners for popups, but those won't display correctly if \"display/window/per_pixel_transparency/allowed\" isn't enabled in the Project Settings, nor if it isn't supported."));
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
#endif
|
||||
|
||||
void PopupPanel::_input_from_window(const Ref<InputEvent> &p_event) {
|
||||
if (p_event.is_valid()) {
|
||||
if (!get_flag(FLAG_POPUP)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseButton> b = p_event;
|
||||
// Hide it if the shadows have been clicked.
|
||||
if (b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::LEFT) {
|
||||
Rect2 panel_area = panel->get_global_rect();
|
||||
float win_scale = get_content_scale_factor();
|
||||
panel_area.position *= win_scale;
|
||||
panel_area.size *= win_scale;
|
||||
if (!panel_area.has_point(b->get_position())) {
|
||||
_close_pressed();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WARN_PRINT_ONCE("PopupPanel has received an invalid InputEvent. Consider filtering out invalid events.");
|
||||
}
|
||||
|
||||
Popup::_input_from_window(p_event);
|
||||
}
|
||||
|
||||
Size2 PopupPanel::_get_contents_minimum_size() const {
|
||||
Size2 ms;
|
||||
|
||||
|
|
@ -239,17 +280,77 @@ Size2 PopupPanel::_get_contents_minimum_size() const {
|
|||
ms = cms.max(ms);
|
||||
}
|
||||
|
||||
// Take shadows into account.
|
||||
ms.width += panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT);
|
||||
ms.height += panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM);
|
||||
|
||||
return ms + theme_cache.panel_style->get_minimum_size();
|
||||
}
|
||||
|
||||
void PopupPanel::_update_child_rects() {
|
||||
Rect2i PopupPanel::_popup_adjust_rect() const {
|
||||
Rect2i current = Popup::_popup_adjust_rect();
|
||||
if (current == Rect2i()) {
|
||||
return current;
|
||||
}
|
||||
|
||||
pre_popup_rect = current;
|
||||
|
||||
_update_shadow_offsets();
|
||||
_update_child_rects();
|
||||
|
||||
if (is_layout_rtl()) {
|
||||
current.position -= Vector2(ABS(panel->get_offset(SIDE_RIGHT)), panel->get_offset(SIDE_TOP)) * get_content_scale_factor();
|
||||
} else {
|
||||
current.position -= Vector2(panel->get_offset(SIDE_LEFT), panel->get_offset(SIDE_TOP)) * get_content_scale_factor();
|
||||
}
|
||||
current.size += Vector2(panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT), panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM)) * get_content_scale_factor();
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
void PopupPanel::_update_shadow_offsets() const {
|
||||
if (!DisplayServer::get_singleton()->is_window_transparency_available() && !is_embedded()) {
|
||||
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const Ref<StyleBoxFlat> sb = theme_cache.panel_style;
|
||||
if (sb.is_null()) {
|
||||
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const int shadow_size = sb->get_shadow_size();
|
||||
if (shadow_size == 0) {
|
||||
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Offset the background panel so it leaves space inside the window for the shadows to be drawn.
|
||||
const Point2 shadow_offset = sb->get_shadow_offset();
|
||||
if (is_layout_rtl()) {
|
||||
panel->set_offset(SIDE_LEFT, shadow_size + shadow_offset.x);
|
||||
panel->set_offset(SIDE_RIGHT, -shadow_size + shadow_offset.x);
|
||||
} else {
|
||||
panel->set_offset(SIDE_LEFT, shadow_size - shadow_offset.x);
|
||||
panel->set_offset(SIDE_RIGHT, -shadow_size - shadow_offset.x);
|
||||
}
|
||||
panel->set_offset(SIDE_TOP, shadow_size - shadow_offset.y);
|
||||
panel->set_offset(SIDE_BOTTOM, -shadow_size - shadow_offset.y);
|
||||
}
|
||||
|
||||
void PopupPanel::_update_child_rects() const {
|
||||
Vector2 cpos(theme_cache.panel_style->get_offset());
|
||||
Vector2 panel_size = Vector2(get_size()) / get_content_scale_factor();
|
||||
Vector2 csize = panel_size - theme_cache.panel_style->get_minimum_size();
|
||||
cpos += Vector2(is_layout_rtl() ? -panel->get_offset(SIDE_RIGHT) : panel->get_offset(SIDE_LEFT), panel->get_offset(SIDE_TOP));
|
||||
|
||||
Vector2 csize = Vector2(get_size()) / get_content_scale_factor() - theme_cache.panel_style->get_minimum_size();
|
||||
// Trim shadows.
|
||||
csize.width -= panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT);
|
||||
csize.height -= panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM);
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = Object::cast_to<Control>(get_child(i));
|
||||
if (!c) {
|
||||
if (!c || c == panel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -257,26 +358,63 @@ void PopupPanel::_update_child_rects() {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (c == panel) {
|
||||
c->set_position(Vector2());
|
||||
c->set_size(panel_size);
|
||||
} else {
|
||||
c->set_position(cpos);
|
||||
c->set_size(csize);
|
||||
}
|
||||
c->set_position(cpos);
|
||||
c->set_size(csize);
|
||||
}
|
||||
}
|
||||
|
||||
void PopupPanel::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
panel->add_theme_style_override(SceneStringName(panel), theme_cache.panel_style);
|
||||
|
||||
if (is_visible()) {
|
||||
_update_shadow_offsets();
|
||||
}
|
||||
|
||||
_update_child_rects();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
update_configuration_warnings();
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
|
||||
if (is_visible()) {
|
||||
_update_shadow_offsets();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (!is_visible()) {
|
||||
// Remove the extra space used by the shadows, so they can be ignored when the popup is hidden.
|
||||
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
|
||||
_update_child_rects();
|
||||
|
||||
if (pre_popup_rect != Rect2i()) {
|
||||
set_position(pre_popup_rect.position);
|
||||
set_size(pre_popup_rect.size);
|
||||
|
||||
pre_popup_rect = Rect2i();
|
||||
}
|
||||
} else if (pre_popup_rect == Rect2i()) {
|
||||
// The popup was made visible directly (without `popup_*()`), so just update the offsets without touching the rect.
|
||||
_update_shadow_offsets();
|
||||
_update_child_rects();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_WM_SIZE_CHANGED: {
|
||||
_update_child_rects();
|
||||
|
||||
if (is_visible()) {
|
||||
const Vector2i offsets = Vector2i(panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT), panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM));
|
||||
// Check if the size actually changed.
|
||||
if (pre_popup_rect.size + offsets != get_size()) {
|
||||
// Play safe, and stick with the new size.
|
||||
pre_popup_rect = Rect2i();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
|
@ -286,6 +424,13 @@ void PopupPanel::_bind_methods() {
|
|||
}
|
||||
|
||||
PopupPanel::PopupPanel() {
|
||||
set_flag(FLAG_TRANSPARENT, true);
|
||||
|
||||
panel = memnew(Panel);
|
||||
panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
add_child(panel, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp((Node *)this, &Node::update_configuration_warnings));
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,8 +85,15 @@ class PopupPanel : public Popup {
|
|||
Ref<StyleBox> panel_style;
|
||||
} theme_cache;
|
||||
|
||||
mutable Rect2i pre_popup_rect;
|
||||
|
||||
protected:
|
||||
void _update_child_rects();
|
||||
virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
virtual Rect2i _popup_adjust_rect() const override;
|
||||
|
||||
void _update_shadow_offsets() const;
|
||||
void _update_child_rects() const;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
|
@ -94,6 +101,10 @@ protected:
|
|||
virtual Size2 _get_contents_minimum_size() const override;
|
||||
|
||||
public:
|
||||
#ifdef TOOLS_ENABLED
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
#endif
|
||||
|
||||
PopupPanel();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@
|
|||
#include "core/input/input.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/gui/menu_bar.h"
|
||||
#include "scene/gui/panel_container.h"
|
||||
#include "scene/resources/style_box_flat.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
HashMap<NativeMenu::SystemMenus, PopupMenu *> PopupMenu::system_menus;
|
||||
|
|
@ -223,6 +223,9 @@ Size2 PopupMenu::_get_item_icon_size(int p_idx) const {
|
|||
Size2 PopupMenu::_get_contents_minimum_size() const {
|
||||
Size2 minsize = theme_cache.panel_style->get_minimum_size();
|
||||
minsize.width += scroll_container->get_v_scroll_bar()->get_size().width;
|
||||
// Take shadows into account.
|
||||
minsize.width += panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT);
|
||||
minsize.height += panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM);
|
||||
|
||||
float max_w = 0.0;
|
||||
float icon_w = 0.0;
|
||||
|
|
@ -232,7 +235,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
|
|||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
Size2 item_size;
|
||||
const_cast<PopupMenu *>(this)->_shape_item(i);
|
||||
_shape_item(i);
|
||||
|
||||
Size2 icon_size = _get_item_icon_size(i);
|
||||
item_size.height = _get_item_height(i);
|
||||
|
|
@ -313,17 +316,32 @@ int PopupMenu::_get_items_total_height() const {
|
|||
}
|
||||
|
||||
int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
|
||||
// Make the item area exclude shadows and the vertical margins and scrollbar.
|
||||
Rect2 item_clickable_area = panel->get_global_rect();
|
||||
if (scroll_container->get_v_scroll_bar()->is_visible_in_tree()) {
|
||||
const int scroll_width = scroll_container->get_v_scroll_bar()->get_size().width;
|
||||
if (is_layout_rtl()) {
|
||||
item_clickable_area.position.x += scroll_width;
|
||||
item_clickable_area.size.width -= scroll_width;
|
||||
}
|
||||
item_clickable_area.size.width -= scroll_width;
|
||||
}
|
||||
float win_scale = get_content_scale_factor();
|
||||
if (p_over.x < 0 || p_over.x >= get_size().width * win_scale || p_over.y < theme_cache.panel_style->get_margin(Side::SIDE_TOP) * win_scale) {
|
||||
item_clickable_area.position.x += theme_cache.panel_style->get_margin(SIDE_LEFT);
|
||||
item_clickable_area.position.y += theme_cache.panel_style->get_margin(SIDE_TOP);
|
||||
item_clickable_area.position *= win_scale;
|
||||
item_clickable_area.size.y -= theme_cache.panel_style->get_margin(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_BOTTOM);
|
||||
item_clickable_area.size *= win_scale;
|
||||
|
||||
if (!item_clickable_area.has_point(p_over)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Point2 ofs = Point2(0, theme_cache.v_separation * 0.5) * win_scale;
|
||||
|
||||
float ofs = item_clickable_area.position.y + (float)theme_cache.v_separation * win_scale * 0.5;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ofs.y += i > 0 ? (float)theme_cache.v_separation * win_scale : (float)theme_cache.v_separation * win_scale * 0.5;
|
||||
ofs.y += _get_item_height(i) * win_scale;
|
||||
if (p_over.y - control->get_position().y * win_scale < ofs.y) {
|
||||
ofs += i > 0 ? (float)theme_cache.v_separation * win_scale : (float)theme_cache.v_separation * win_scale * 0.5;
|
||||
ofs += _get_item_height(i) * win_scale;
|
||||
if (p_over.y - control->get_position().y * win_scale < ofs) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
@ -332,35 +350,43 @@ int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
|
|||
}
|
||||
|
||||
void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
|
||||
Popup *submenu_popup = items[p_over].submenu;
|
||||
PopupMenu *submenu_popup = items[p_over].submenu;
|
||||
if (submenu_popup->is_visible()) {
|
||||
return; // Already visible.
|
||||
}
|
||||
|
||||
Point2 this_pos = get_position();
|
||||
const float win_scale = get_content_scale_factor();
|
||||
|
||||
const Point2 panel_ofs_start = Point2(panel->get_offset(SIDE_LEFT), panel->get_offset(SIDE_TOP)) * win_scale;
|
||||
const Point2 panel_ofs_end = Point2(panel->get_offset(SIDE_RIGHT), panel->get_offset(SIDE_BOTTOM)).abs() * win_scale;
|
||||
|
||||
const Point2 this_pos = get_position() + Point2(0, panel_ofs_start.y + theme_cache.panel_style->get_margin(SIDE_TOP) * win_scale);
|
||||
Rect2 this_rect(this_pos, get_size());
|
||||
|
||||
float scroll_offset = control->get_position().y;
|
||||
float scaled_ofs_cache = items[p_over]._ofs_cache * get_content_scale_factor();
|
||||
float scaled_height_cache = items[p_over]._height_cache * get_content_scale_factor();
|
||||
const float scroll_offset = control->get_position().y;
|
||||
const float scaled_ofs_cache = items[p_over]._ofs_cache * win_scale;
|
||||
const float scaled_height_cache = items[p_over]._height_cache * win_scale;
|
||||
|
||||
submenu_popup->reset_size(); // Shrink the popup size to its contents.
|
||||
Size2 submenu_size = submenu_popup->get_size();
|
||||
const Size2 submenu_size = submenu_popup->get_size();
|
||||
|
||||
Point2 submenu_pos;
|
||||
if (control->is_layout_rtl()) {
|
||||
submenu_pos = this_pos + Point2(-submenu_size.width, scaled_ofs_cache + scroll_offset - theme_cache.v_separation / 2);
|
||||
// Calculate the submenu's position.
|
||||
Point2 submenu_pos(0, -submenu_popup->get_theme_stylebox(SceneStringName(panel))->get_margin(SIDE_TOP) * submenu_popup->get_content_scale_factor());
|
||||
Rect2i screen_rect = is_embedded() ? Rect2i(get_embedder()->get_visible_rect()) : get_parent_rect();
|
||||
if (is_layout_rtl()) {
|
||||
submenu_pos += this_pos + Point2(-submenu_size.width + panel_ofs_end.x, scaled_ofs_cache + scroll_offset - theme_cache.v_separation / 2);
|
||||
if (submenu_pos.x < screen_rect.position.x) {
|
||||
submenu_pos.x = this_pos.x + this_rect.size.width - panel_ofs_start.x;
|
||||
}
|
||||
|
||||
this_rect.position.x += panel_ofs_end.x;
|
||||
} else {
|
||||
submenu_pos = this_pos + Point2(this_rect.size.width, scaled_ofs_cache + scroll_offset - theme_cache.v_separation / 2);
|
||||
}
|
||||
submenu_pos += this_pos + Point2(this_rect.size.width - panel_ofs_end.x, scaled_ofs_cache + scroll_offset - theme_cache.v_separation / 2);
|
||||
if (submenu_pos.x + submenu_size.width > screen_rect.position.x + screen_rect.size.width) {
|
||||
submenu_pos.x = this_pos.x - submenu_size.width + panel_ofs_start.x;
|
||||
}
|
||||
|
||||
// Fix pos if going outside parent rect.
|
||||
if (submenu_pos.x < get_parent_rect().position.x) {
|
||||
submenu_pos.x = this_pos.x + submenu_size.width;
|
||||
}
|
||||
|
||||
if (submenu_pos.x + submenu_size.width > get_parent_rect().position.x + get_parent_rect().size.width) {
|
||||
submenu_pos.x = this_pos.x - submenu_size.width;
|
||||
this_rect.position.x += panel_ofs_start.x;
|
||||
}
|
||||
|
||||
submenu_popup->set_position(submenu_pos);
|
||||
|
|
@ -387,9 +413,7 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
|
|||
|
||||
// Set autohide areas.
|
||||
|
||||
Rect2 safe_area = this_rect;
|
||||
safe_area.position.y += scaled_ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2;
|
||||
safe_area.size.y = scaled_height_cache + theme_cache.v_separation;
|
||||
const Rect2 safe_area(get_position(), get_size());
|
||||
Viewport *vp = submenu_popup->get_embedder();
|
||||
if (vp) {
|
||||
vp->subwindow_set_popup_safe_rect(submenu_popup, safe_area);
|
||||
|
|
@ -397,16 +421,18 @@ void PopupMenu::_activate_submenu(int p_over, bool p_by_keyboard) {
|
|||
DisplayServer::get_singleton()->window_set_popup_safe_rect(submenu_popup->get_window_id(), safe_area);
|
||||
}
|
||||
|
||||
// Make the position of the parent popup relative to submenu popup.
|
||||
this_rect.position = this_rect.position - submenu_pum->get_position();
|
||||
this_rect.position -= submenu_pum->get_position(); // Make the position of the parent popup relative to submenu popup.
|
||||
this_rect.size.width -= panel_ofs_start.x + panel_ofs_end.x;
|
||||
this_rect.size.height -= panel_ofs_end.y + (theme_cache.panel_style->get_margin(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_BOTTOM)) * win_scale;
|
||||
|
||||
// Autohide area above the submenu item.
|
||||
submenu_pum->clear_autohide_areas();
|
||||
submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, scaled_ofs_cache + scroll_offset + theme_cache.panel_style->get_offset().height - theme_cache.v_separation / 2));
|
||||
submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y - theme_cache.panel_style->get_margin(SIDE_TOP) * win_scale,
|
||||
this_rect.size.x, scaled_ofs_cache + scroll_offset + theme_cache.panel_style->get_margin(SIDE_TOP) * win_scale - theme_cache.v_separation / 2));
|
||||
|
||||
// If there is an area below the submenu item, add an autohide area there.
|
||||
if (scaled_ofs_cache + scaled_height_cache + scroll_offset <= control->get_size().height) {
|
||||
int from = scaled_ofs_cache + scaled_height_cache + scroll_offset + theme_cache.v_separation / 2 + theme_cache.panel_style->get_offset().height;
|
||||
if (scaled_ofs_cache + scaled_height_cache + scroll_offset <= control->get_size().height * win_scale) {
|
||||
const int from = scaled_ofs_cache + scaled_height_cache + scroll_offset + theme_cache.v_separation / 2;
|
||||
submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
|
||||
}
|
||||
}
|
||||
|
|
@ -446,7 +472,7 @@ void PopupMenu::_input_from_window(const Ref<InputEvent> &p_event) {
|
|||
if (p_event.is_valid()) {
|
||||
_input_from_window_internal(p_event);
|
||||
} else {
|
||||
WARN_PRINT_ONCE("PopupMenu has received an invalid InputEvent. Consider filtering invalid events out.");
|
||||
WARN_PRINT_ONCE("PopupMenu has received an invalid InputEvent. Consider filtering out invalid events.");
|
||||
}
|
||||
Popup::_input_from_window(p_event);
|
||||
}
|
||||
|
|
@ -474,7 +500,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
for (int i = search_from; i < items.size(); i++) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), i);
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
|
|
@ -488,7 +514,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
for (int i = 0; i < search_from; i++) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), i);
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
|
|
@ -512,7 +538,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
for (int i = search_from; i >= 0; i--) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), i);
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
|
|
@ -526,7 +552,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
for (int i = items.size() - 1; i >= search_from; i--) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), i);
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
|
|
@ -570,15 +596,23 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
|
||||
// Make an area which does not include v scrollbar, so that items are not activated when dragging scrollbar.
|
||||
Rect2 item_clickable_area = scroll_container->get_rect();
|
||||
// Make the item area exclude shadows and the vertical margins and scrollbar.
|
||||
Rect2 item_clickable_area = panel->get_global_rect();
|
||||
if (scroll_container->get_v_scroll_bar()->is_visible_in_tree()) {
|
||||
int scroll_width = scroll_container->get_v_scroll_bar()->get_size().width;
|
||||
if (is_layout_rtl()) {
|
||||
item_clickable_area.position.x += scroll_container->get_v_scroll_bar()->get_size().width;
|
||||
item_clickable_area.position.x += scroll_width;
|
||||
item_clickable_area.size.width -= scroll_width;
|
||||
}
|
||||
item_clickable_area.size.width -= scroll_container->get_v_scroll_bar()->get_size().width;
|
||||
item_clickable_area.size.width -= scroll_width;
|
||||
}
|
||||
item_clickable_area.size = item_clickable_area.size * get_content_scale_factor();
|
||||
|
||||
float win_scale = get_content_scale_factor();
|
||||
item_clickable_area.position.x += theme_cache.panel_style->get_margin(SIDE_LEFT);
|
||||
item_clickable_area.position.y += theme_cache.panel_style->get_margin(SIDE_TOP);
|
||||
item_clickable_area.position *= win_scale;
|
||||
item_clickable_area.size.y -= theme_cache.panel_style->get_margin(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_BOTTOM);
|
||||
item_clickable_area.size *= win_scale;
|
||||
|
||||
Ref<InputEventMouseButton> b = p_event;
|
||||
|
||||
|
|
@ -592,9 +626,21 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
during_grabbed_click = false;
|
||||
is_scrolling = is_layout_rtl() ? b->get_position().x < item_clickable_area.position.x : b->get_position().x > item_clickable_area.size.width;
|
||||
|
||||
// Hide it if the shadows have been clicked.
|
||||
if (get_flag(FLAG_POPUP)) {
|
||||
Rect2 panel_area = panel->get_global_rect();
|
||||
panel_area.position *= win_scale;
|
||||
panel_area.size *= win_scale;
|
||||
if (!panel_area.has_point(b->get_position())) {
|
||||
_close_pressed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!item_clickable_area.has_point(b->get_position())) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mouse_over_update(b->get_position());
|
||||
} else {
|
||||
if (is_scrolling) {
|
||||
|
|
@ -608,6 +654,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
if (!item_clickable_area.has_point(b->get_position())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable clicks under a time threshold to avoid selection right when opening the popup.
|
||||
if (was_during_grabbed_click && OS::get_singleton()->get_ticks_msec() - popup_time_msec < 400) {
|
||||
return;
|
||||
|
|
@ -643,7 +690,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
activated_by_keyboard = false;
|
||||
|
||||
for (const Rect2 &E : autohide_areas) {
|
||||
if (!Rect2(Point2(), get_size()).has_point(m->get_position()) && E.has_point(m->get_position())) {
|
||||
if (!scroll_container->get_global_rect().has_point(m->get_position()) && E.has_point(m->get_position())) {
|
||||
// The mouse left the safe area, prepare to close.
|
||||
_close_pressed();
|
||||
return;
|
||||
|
|
@ -655,7 +702,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
minimum_lifetime_timer->stop();
|
||||
}
|
||||
|
||||
if (!item_clickable_area.has_point(m->get_position())) {
|
||||
if (mouse_over == -1 && !item_clickable_area.has_point(m->get_position())) {
|
||||
return;
|
||||
}
|
||||
_mouse_over_update(m->get_position());
|
||||
|
|
@ -692,7 +739,7 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
|
||||
if (items[i].text.findn(search_string) == 0) {
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), i);
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
|
|
@ -946,7 +993,7 @@ void PopupMenu::_close_pressed() {
|
|||
}
|
||||
}
|
||||
|
||||
void PopupMenu::_shape_item(int p_idx) {
|
||||
void PopupMenu::_shape_item(int p_idx) const {
|
||||
if (items.write[p_idx].dirty) {
|
||||
items.write[p_idx].text_buf->clear();
|
||||
|
||||
|
|
@ -971,6 +1018,57 @@ void PopupMenu::_menu_changed() {
|
|||
emit_signal(SNAME("menu_changed"));
|
||||
}
|
||||
|
||||
void PopupMenu::_update_shadow_offsets() const {
|
||||
if (!DisplayServer::get_singleton()->is_window_transparency_available() && !is_embedded()) {
|
||||
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<StyleBoxFlat> sb = theme_cache.panel_style;
|
||||
if (sb.is_null()) {
|
||||
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const int shadow_size = sb->get_shadow_size();
|
||||
if (shadow_size == 0) {
|
||||
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Offset the background panel so it leaves space inside the window for the shadows to be drawn.
|
||||
const Point2 shadow_offset = sb->get_shadow_offset();
|
||||
if (is_layout_rtl()) {
|
||||
panel->set_offset(SIDE_LEFT, shadow_size + shadow_offset.x);
|
||||
panel->set_offset(SIDE_RIGHT, -shadow_size + shadow_offset.x);
|
||||
} else {
|
||||
panel->set_offset(SIDE_LEFT, shadow_size - shadow_offset.x);
|
||||
panel->set_offset(SIDE_RIGHT, -shadow_size - shadow_offset.x);
|
||||
}
|
||||
panel->set_offset(SIDE_TOP, shadow_size - shadow_offset.y);
|
||||
panel->set_offset(SIDE_BOTTOM, -shadow_size - shadow_offset.y);
|
||||
}
|
||||
|
||||
Rect2i PopupMenu::_popup_adjust_rect() const {
|
||||
Rect2i current = Popup::_popup_adjust_rect();
|
||||
if (current == Rect2i()) {
|
||||
return current;
|
||||
}
|
||||
|
||||
pre_popup_rect = current;
|
||||
|
||||
_update_shadow_offsets();
|
||||
|
||||
if (is_layout_rtl()) {
|
||||
current.position -= Vector2(ABS(panel->get_offset(SIDE_RIGHT)), panel->get_offset(SIDE_TOP)) * get_content_scale_factor();
|
||||
} else {
|
||||
current.position -= Vector2(panel->get_offset(SIDE_LEFT), panel->get_offset(SIDE_TOP)) * get_content_scale_factor();
|
||||
}
|
||||
current.size += Vector2(panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT), panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM)) * get_content_scale_factor();
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
void PopupMenu::add_child_notify(Node *p_child) {
|
||||
Window::add_child_notify(p_child);
|
||||
|
||||
|
|
@ -1026,12 +1124,20 @@ void PopupMenu::_notification(int p_what) {
|
|||
}
|
||||
} break;
|
||||
|
||||
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
scroll_container->add_theme_style_override(SceneStringName(panel), theme_cache.panel_style);
|
||||
panel->add_theme_style_override(SceneStringName(panel), theme_cache.panel_style);
|
||||
|
||||
if (is_visible()) {
|
||||
_update_shadow_offsets();
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
update_configuration_warnings();
|
||||
#endif
|
||||
|
||||
[[fallthrough]];
|
||||
}
|
||||
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
NativeMenu *nmenu = NativeMenu::get_singleton();
|
||||
bool is_global = global_menu.is_valid();
|
||||
|
|
@ -1064,6 +1170,17 @@ void PopupMenu::_notification(int p_what) {
|
|||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_WM_SIZE_CHANGED: {
|
||||
if (is_visible()) {
|
||||
const Vector2i offsets = Vector2i(panel->get_offset(SIDE_LEFT) - panel->get_offset(SIDE_RIGHT), panel->get_offset(SIDE_TOP) - panel->get_offset(SIDE_BOTTOM));
|
||||
// Check if the size actually changed.
|
||||
if (pre_popup_rect.size + offsets != get_size()) {
|
||||
// Play safe, and stick with the new size.
|
||||
pre_popup_rect = Rect2i();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_POST_POPUP: {
|
||||
popup_time_msec = OS::get_singleton()->get_ticks_msec();
|
||||
initial_button_mask = Input::get_singleton()->get_mouse_button_mask();
|
||||
|
|
@ -1091,7 +1208,7 @@ void PopupMenu::_notification(int p_what) {
|
|||
for (int i = search_from; i < items.size(); i++) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), i);
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
control->queue_redraw();
|
||||
match_found = true;
|
||||
|
|
@ -1104,7 +1221,7 @@ void PopupMenu::_notification(int p_what) {
|
|||
for (int i = 0; i < search_from; i++) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), i);
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
control->queue_redraw();
|
||||
break;
|
||||
|
|
@ -1124,7 +1241,7 @@ void PopupMenu::_notification(int p_what) {
|
|||
for (int i = search_from; i >= 0; i--) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), i);
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
control->queue_redraw();
|
||||
match_found = true;
|
||||
|
|
@ -1137,7 +1254,7 @@ void PopupMenu::_notification(int p_what) {
|
|||
for (int i = items.size() - 1; i >= search_from; i--) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), i);
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
control->queue_redraw();
|
||||
break;
|
||||
|
|
@ -1176,10 +1293,25 @@ void PopupMenu::_notification(int p_what) {
|
|||
}
|
||||
|
||||
set_process_internal(false);
|
||||
|
||||
// Remove the extra space used by the shadows, so they can be ignored when the popup is hidden.
|
||||
panel->set_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 0);
|
||||
|
||||
if (pre_popup_rect != Rect2i()) {
|
||||
set_position(pre_popup_rect.position);
|
||||
set_size(pre_popup_rect.size);
|
||||
|
||||
pre_popup_rect = Rect2i();
|
||||
}
|
||||
} else {
|
||||
if (!is_embedded()) {
|
||||
set_process_internal(true);
|
||||
}
|
||||
|
||||
// The popup was made visible directly (without `popup_*()`), so just update the offsets without touching the rect.
|
||||
if (pre_popup_rect == Rect2i()) {
|
||||
_update_shadow_offsets();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
|
@ -2584,6 +2716,21 @@ String PopupMenu::get_tooltip(const Point2 &p_pos) const {
|
|||
return items[over].tooltip;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
PackedStringArray PopupMenu::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Popup::get_configuration_warnings();
|
||||
|
||||
if (!DisplayServer::get_singleton()->is_window_transparency_available() && !GLOBAL_GET("display/window/subwindows/embed_subwindows")) {
|
||||
Ref<StyleBoxFlat> sb = theme_cache.panel_style;
|
||||
if (sb.is_valid() && (sb->get_shadow_size() > 0 || sb->get_corner_radius(CORNER_TOP_LEFT) > 0 || sb->get_corner_radius(CORNER_TOP_RIGHT) > 0 || sb->get_corner_radius(CORNER_BOTTOM_LEFT) > 0 || sb->get_corner_radius(CORNER_BOTTOM_RIGHT) > 0)) {
|
||||
warnings.push_back(RTR("The current theme style has shadows and/or rounded corners for popups, but those won't display correctly if \"display/window/per_pixel_transparency/allowed\" isn't enabled in the Project Settings, nor if it isn't supported."));
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
#endif
|
||||
|
||||
void PopupMenu::add_autohide_area(const Rect2 &p_area) {
|
||||
autohide_areas.push_back(p_area);
|
||||
}
|
||||
|
|
@ -2592,14 +2739,6 @@ void PopupMenu::clear_autohide_areas() {
|
|||
autohide_areas.clear();
|
||||
}
|
||||
|
||||
void PopupMenu::take_mouse_focus() {
|
||||
ERR_FAIL_COND(!is_inside_tree());
|
||||
|
||||
if (get_parent()) {
|
||||
get_parent()->get_viewport()->pass_mouse_focus_to(this, control);
|
||||
}
|
||||
}
|
||||
|
||||
bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) {
|
||||
if (property_helper.property_set_value(p_name, p_value)) {
|
||||
return true;
|
||||
|
|
@ -2823,12 +2962,27 @@ void PopupMenu::_bind_methods() {
|
|||
base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &PopupMenu::set_item_icon, &PopupMenu::get_item_icon);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::INT, "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"), defaults.checkable_type, &PopupMenu::_set_item_checkable_type, &PopupMenu::_get_item_checkable_type);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::BOOL, "checked"), defaults.checked, &PopupMenu::set_item_checked, &PopupMenu::is_item_checked);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), defaults.id, &PopupMenu::set_item_id, &PopupMenu::get_item_id);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_STORE_IF_NULL), defaults.id, &PopupMenu::set_item_id, &PopupMenu::get_item_id);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::BOOL, "disabled"), defaults.disabled, &PopupMenu::set_item_disabled, &PopupMenu::is_item_disabled);
|
||||
base_property_helper.register_property(PropertyInfo(Variant::BOOL, "separator"), defaults.separator, &PopupMenu::set_item_as_separator, &PopupMenu::is_item_separator);
|
||||
PropertyListHelper::register_base_helper(&base_property_helper);
|
||||
}
|
||||
|
||||
void PopupMenu::_native_popup(const Rect2i &p_rect) {
|
||||
Point2i popup_pos = p_rect.position;
|
||||
if (is_embedded()) {
|
||||
popup_pos = get_embedder()->get_screen_transform().xform(popup_pos); // Note: for embedded windows "screen transform" is transform relative to embedder not the actual screen.
|
||||
DisplayServer::WindowID wid = get_window_id();
|
||||
if (wid == DisplayServer::INVALID_WINDOW_ID) {
|
||||
wid = DisplayServer::MAIN_WINDOW_ID;
|
||||
}
|
||||
popup_pos += DisplayServer::get_singleton()->window_get_position(wid);
|
||||
}
|
||||
float win_scale = get_parent_visible_window()->get_content_scale_factor();
|
||||
NativeMenu::get_singleton()->set_minimum_width(global_menu, p_rect.size.x * win_scale);
|
||||
NativeMenu::get_singleton()->popup(global_menu, popup_pos);
|
||||
}
|
||||
|
||||
void PopupMenu::popup(const Rect2i &p_bounds) {
|
||||
bool native = global_menu.is_valid();
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
@ -2838,7 +2992,7 @@ void PopupMenu::popup(const Rect2i &p_bounds) {
|
|||
#endif
|
||||
|
||||
if (native) {
|
||||
NativeMenu::get_singleton()->popup(global_menu, (p_bounds != Rect2i()) ? p_bounds.position : get_position());
|
||||
_native_popup(p_bounds != Rect2i() ? p_bounds : Rect2i(get_position(), Size2i()));
|
||||
} else {
|
||||
set_flag(FLAG_NO_FOCUS, !is_embedded());
|
||||
|
||||
|
|
@ -2866,7 +3020,7 @@ void PopupMenu::set_visible(bool p_visible) {
|
|||
|
||||
if (native) {
|
||||
if (p_visible) {
|
||||
NativeMenu::get_singleton()->popup(global_menu, get_position());
|
||||
_native_popup(Rect2i(get_position(), get_size()));
|
||||
}
|
||||
} else {
|
||||
set_flag(FLAG_NO_FOCUS, !is_embedded());
|
||||
|
|
@ -2876,11 +3030,17 @@ void PopupMenu::set_visible(bool p_visible) {
|
|||
}
|
||||
|
||||
PopupMenu::PopupMenu() {
|
||||
set_flag(FLAG_TRANSPARENT, true);
|
||||
|
||||
// The panel used to draw the panel style.
|
||||
panel = memnew(PanelContainer);
|
||||
panel->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
add_child(panel, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
// Scroll Container
|
||||
scroll_container = memnew(ScrollContainer);
|
||||
scroll_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
scroll_container->set_clip_contents(true);
|
||||
add_child(scroll_container, false, INTERNAL_MODE_FRONT);
|
||||
panel->add_child(scroll_container, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
// The control which will display the items
|
||||
control = memnew(Control);
|
||||
|
|
@ -2904,6 +3064,10 @@ PopupMenu::PopupMenu() {
|
|||
add_child(minimum_lifetime_timer, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
property_helper.setup_for_instance(base_property_helper, this);
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp((Node *)this, &Node::update_configuration_warnings));
|
||||
#endif
|
||||
}
|
||||
|
||||
PopupMenu::~PopupMenu() {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@
|
|||
#include "scene/property_list_helper.h"
|
||||
#include "scene/resources/text_line.h"
|
||||
|
||||
class PanelContainer;
|
||||
|
||||
class PopupMenu : public Popup {
|
||||
GDCLASS(PopupMenu, Popup);
|
||||
|
||||
|
|
@ -94,6 +96,9 @@ class PopupMenu : public Popup {
|
|||
Item(bool p_dummy) {}
|
||||
};
|
||||
|
||||
mutable Rect2i pre_popup_rect;
|
||||
void _update_shadow_offsets() const;
|
||||
|
||||
static inline PropertyListHelper base_property_helper;
|
||||
PropertyListHelper property_helper;
|
||||
|
||||
|
|
@ -112,7 +117,7 @@ class PopupMenu : public Popup {
|
|||
Timer *minimum_lifetime_timer = nullptr;
|
||||
Timer *submenu_timer = nullptr;
|
||||
List<Rect2> autohide_areas;
|
||||
Vector<Item> items;
|
||||
mutable Vector<Item> items;
|
||||
BitField<MouseButtonMask> initial_button_mask;
|
||||
bool during_grabbed_click = false;
|
||||
bool is_scrolling = false;
|
||||
|
|
@ -127,7 +132,7 @@ class PopupMenu : public Popup {
|
|||
int _get_items_total_height() const;
|
||||
Size2 _get_item_icon_size(int p_idx) const;
|
||||
|
||||
void _shape_item(int p_idx);
|
||||
void _shape_item(int p_idx) const;
|
||||
|
||||
void _activate_submenu(int p_over, bool p_by_keyboard = false);
|
||||
void _submenu_timeout();
|
||||
|
|
@ -149,6 +154,7 @@ class PopupMenu : public Popup {
|
|||
uint64_t search_time_msec = 0;
|
||||
String search_string = "";
|
||||
|
||||
PanelContainer *panel = nullptr;
|
||||
ScrollContainer *scroll_container = nullptr;
|
||||
Control *control = nullptr;
|
||||
|
||||
|
|
@ -209,8 +215,11 @@ class PopupMenu : public Popup {
|
|||
bool _set_item_accelerator(int p_index, const Ref<InputEventKey> &p_ie);
|
||||
void _set_item_checkable_type(int p_index, int p_checkable_type);
|
||||
int _get_item_checkable_type(int p_index) const;
|
||||
void _native_popup(const Rect2i &p_rect);
|
||||
|
||||
protected:
|
||||
virtual Rect2i _popup_adjust_rect() const override;
|
||||
|
||||
virtual void add_child_notify(Node *p_child) override;
|
||||
virtual void remove_child_notify(Node *p_child) override;
|
||||
virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
|
||||
|
|
@ -348,6 +357,10 @@ public:
|
|||
|
||||
virtual String get_tooltip(const Point2 &p_pos) const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
#endif
|
||||
|
||||
void add_autohide_area(const Rect2 &p_area);
|
||||
void clear_autohide_areas();
|
||||
|
||||
|
|
@ -369,8 +382,6 @@ public:
|
|||
virtual void popup(const Rect2i &p_bounds = Rect2i()) override;
|
||||
virtual void set_visible(bool p_visible) override;
|
||||
|
||||
void take_mouse_focus();
|
||||
|
||||
PopupMenu();
|
||||
~PopupMenu();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ void ProgressBar::_notification(int p_what) {
|
|||
Size2 size = get_size();
|
||||
real_t fill_size = MIN(size.width, size.height) * 2;
|
||||
|
||||
if (Engine::get_singleton()->is_editor_hint() && !editor_preview_indeterminate) {
|
||||
if (is_part_of_edited_scene() && !editor_preview_indeterminate) {
|
||||
// Center the filled bar when we're not previewing the animation.
|
||||
_inderminate_fill_progress = (MAX(size.width, size.height) / 2) + (fill_size / 2);
|
||||
}
|
||||
|
|
@ -217,7 +217,7 @@ void ProgressBar::set_indeterminate(bool p_indeterminate) {
|
|||
indeterminate = p_indeterminate;
|
||||
_inderminate_fill_progress = 0;
|
||||
|
||||
bool should_process = !Engine::get_singleton()->is_editor_hint() || editor_preview_indeterminate;
|
||||
bool should_process = !is_part_of_edited_scene() || editor_preview_indeterminate;
|
||||
set_process_internal(indeterminate && should_process);
|
||||
|
||||
notify_property_list_changed();
|
||||
|
|
@ -235,7 +235,7 @@ void ProgressBar::set_editor_preview_indeterminate(bool p_preview_indeterminate)
|
|||
}
|
||||
editor_preview_indeterminate = p_preview_indeterminate;
|
||||
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
if (is_part_of_edited_scene()) {
|
||||
_inderminate_fill_progress = 0;
|
||||
set_process_internal(indeterminate && editor_preview_indeterminate);
|
||||
queue_redraw();
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#include "range.h"
|
||||
|
||||
PackedStringArray Range::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Control::get_configuration_warnings();
|
||||
|
||||
if (shared->exp_ratio && shared->min <= 0) {
|
||||
warnings.push_back(RTR("If \"Exp Edit\" is enabled, \"Min Value\" must be greater than 0."));
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class Range : public Control {
|
|||
|
||||
protected:
|
||||
virtual void _value_changed(double p_value);
|
||||
void _notify_shared_value_changed() { shared->emit_value_changed(); };
|
||||
void _notify_shared_value_changed() { shared->emit_value_changed(); }
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
|
|
|
|||
|
|
@ -79,20 +79,20 @@ public:
|
|||
Color get_color() { return color; }
|
||||
void set_color(Color p_color) { color = p_color; }
|
||||
|
||||
uint32_t get_glyph_index() const { return glyph_index; };
|
||||
void set_glyph_index(uint32_t p_glyph_index) { glyph_index = p_glyph_index; };
|
||||
uint32_t get_glyph_index() const { return glyph_index; }
|
||||
void set_glyph_index(uint32_t p_glyph_index) { glyph_index = p_glyph_index; }
|
||||
|
||||
uint16_t get_glyph_flags() const { return glyph_flags; };
|
||||
void set_glyph_flags(uint16_t p_glyph_flags) { glyph_flags = p_glyph_flags; };
|
||||
uint16_t get_glyph_flags() const { return glyph_flags; }
|
||||
void set_glyph_flags(uint16_t p_glyph_flags) { glyph_flags = p_glyph_flags; }
|
||||
|
||||
uint8_t get_glyph_count() const { return glyph_count; };
|
||||
void set_glyph_count(uint8_t p_glyph_count) { glyph_count = p_glyph_count; };
|
||||
uint8_t get_glyph_count() const { return glyph_count; }
|
||||
void set_glyph_count(uint8_t p_glyph_count) { glyph_count = p_glyph_count; }
|
||||
|
||||
int32_t get_relative_index() const { return relative_index; };
|
||||
void set_relative_index(int32_t p_relative_index) { relative_index = p_relative_index; };
|
||||
int32_t get_relative_index() const { return relative_index; }
|
||||
void set_relative_index(int32_t p_relative_index) { relative_index = p_relative_index; }
|
||||
|
||||
RID get_font() const { return font; };
|
||||
void set_font(RID p_font) { font = p_font; };
|
||||
RID get_font() const { return font; }
|
||||
void set_font(RID p_font) { font = p_font; }
|
||||
|
||||
Dictionary get_environment() { return environment; }
|
||||
void set_environment(Dictionary p_environment) { environment = p_environment; }
|
||||
|
|
|
|||
|
|
@ -30,8 +30,24 @@
|
|||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
void RichTextLabel::_push_font_bind_compat_79053(const Ref<Font> &p_font, int p_size) {
|
||||
push_font(p_font, p_size);
|
||||
}
|
||||
|
||||
void RichTextLabel::_set_table_column_expand_bind_compat_79053(int p_column, bool p_expand, int p_ratio) {
|
||||
set_table_column_expand(p_column, p_expand, p_ratio, true);
|
||||
}
|
||||
|
||||
void RichTextLabel::_set_table_column_expand_bind_compat_101482(int p_column, bool p_expand, int p_ratio) {
|
||||
set_table_column_expand(p_column, p_expand, p_ratio, true);
|
||||
}
|
||||
|
||||
void RichTextLabel::_push_meta_bind_compat_99481(const Variant &p_meta, MetaUnderline p_underline_mode) {
|
||||
push_meta(p_meta, p_underline_mode, String());
|
||||
}
|
||||
|
||||
void RichTextLabel::_push_meta_bind_compat_89024(const Variant &p_meta) {
|
||||
push_meta(p_meta, RichTextLabel::MetaUnderline::META_UNDERLINE_ALWAYS);
|
||||
push_meta(p_meta, RichTextLabel::MetaUnderline::META_UNDERLINE_ALWAYS, String());
|
||||
}
|
||||
|
||||
void RichTextLabel::_add_image_bind_compat_80410(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region) {
|
||||
|
|
@ -43,6 +59,10 @@ bool RichTextLabel::_remove_paragraph_bind_compat_91098(int p_paragraph) {
|
|||
}
|
||||
|
||||
void RichTextLabel::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::_push_font_bind_compat_79053);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::_set_table_column_expand_bind_compat_79053);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::_set_table_column_expand_bind_compat_101482, DEFVAL(1));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("push_meta", "data", "underline_mode"), &RichTextLabel::_push_meta_bind_compat_99481, DEFVAL(META_UNDERLINE_ALWAYS));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("push_meta", "data"), &RichTextLabel::_push_meta_bind_compat_89024);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region"), &RichTextLabel::_add_image_bind_compat_80410, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2()));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("remove_paragraph", "paragraph"), &RichTextLabel::_remove_paragraph_bind_compat_91098);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -132,9 +132,13 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _push_font_bind_compat_79053(const Ref<Font> &p_font, int p_size);
|
||||
void _set_table_column_expand_bind_compat_79053(int p_column, bool p_expand, int p_ratio);
|
||||
void _push_meta_bind_compat_99481(const Variant &p_meta, MetaUnderline p_underline_mode);
|
||||
void _push_meta_bind_compat_89024(const Variant &p_meta);
|
||||
void _add_image_bind_compat_80410(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region);
|
||||
bool _remove_paragraph_bind_compat_91098(int p_paragraph);
|
||||
void _set_table_column_expand_bind_compat_101482(int p_column, bool p_expand, int p_ratio);
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
|
|
@ -152,6 +156,7 @@ private:
|
|||
Color dc_ol_color;
|
||||
|
||||
Vector2 offset;
|
||||
float indent = 0.0;
|
||||
int char_offset = 0;
|
||||
int char_count = 0;
|
||||
|
||||
|
|
@ -204,6 +209,7 @@ private:
|
|||
Size2 min_size_over = Size2(-1, -1);
|
||||
Size2 max_size_over = Size2(-1, -1);
|
||||
Rect2 padding;
|
||||
int indent_level = 0;
|
||||
|
||||
ItemFrame() {
|
||||
type = ITEM_FRAME;
|
||||
|
|
@ -291,6 +297,7 @@ private:
|
|||
struct ItemMeta : public Item {
|
||||
Variant meta;
|
||||
MetaUnderline underline = META_UNDERLINE_ALWAYS;
|
||||
String tooltip;
|
||||
ItemMeta() { type = ITEM_META; }
|
||||
};
|
||||
|
||||
|
|
@ -335,6 +342,7 @@ private:
|
|||
struct ItemTable : public Item {
|
||||
struct Column {
|
||||
bool expand = false;
|
||||
bool shrink = true;
|
||||
int expand_ratio = 0;
|
||||
int min_width = 0;
|
||||
int max_width = 0;
|
||||
|
|
@ -409,6 +417,7 @@ private:
|
|||
float saturation = 0.8f;
|
||||
float value = 0.8f;
|
||||
float frequency = 1.0f;
|
||||
float speed = 1.0f;
|
||||
|
||||
ItemRainbow() { type = ITEM_RAINBOW; }
|
||||
};
|
||||
|
|
@ -455,6 +464,7 @@ private:
|
|||
std::atomic<bool> updating;
|
||||
std::atomic<bool> validating;
|
||||
std::atomic<double> loaded;
|
||||
std::atomic<bool> parsing_bbcode;
|
||||
|
||||
uint64_t loading_started = 0;
|
||||
int progress_delay = 1000;
|
||||
|
|
@ -481,7 +491,9 @@ private:
|
|||
bool use_selected_font_color = false;
|
||||
|
||||
HorizontalAlignment default_alignment = HORIZONTAL_ALIGNMENT_LEFT;
|
||||
VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_TOP;
|
||||
BitField<TextServer::JustificationFlag> default_jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE;
|
||||
PackedFloat32Array default_tab_stops;
|
||||
|
||||
ItemMeta *meta_hovering = nullptr;
|
||||
Variant current_meta;
|
||||
|
|
@ -503,6 +515,7 @@ private:
|
|||
void _texture_changed(RID p_item);
|
||||
|
||||
RID_PtrOwner<Item> items;
|
||||
List<String> tag_stack;
|
||||
|
||||
String language;
|
||||
TextDirection text_direction = TEXT_DIRECTION_AUTO;
|
||||
|
|
@ -525,12 +538,14 @@ private:
|
|||
Item *to_item = nullptr;
|
||||
int to_char = 0;
|
||||
|
||||
bool double_click = false; // Selecting whole words?
|
||||
bool active = false; // anything selected? i.e. from, to, etc. valid?
|
||||
bool enabled = false; // allow selections?
|
||||
bool drag_attempt = false;
|
||||
};
|
||||
|
||||
Selection selection;
|
||||
Callable selection_modifier;
|
||||
bool deselect_on_focus_loss_enabled = true;
|
||||
bool drag_and_drop_selection_enabled = true;
|
||||
|
||||
|
|
@ -560,8 +575,8 @@ private:
|
|||
void _set_table_size(ItemTable *p_table, int p_available_width);
|
||||
|
||||
void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size);
|
||||
int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs);
|
||||
float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false, bool p_meta = false);
|
||||
int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs);
|
||||
float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool p_table = false, bool p_meta = false);
|
||||
|
||||
String _roman(int p_num, bool p_capitalize) const;
|
||||
String _letters(int p_num, bool p_capitalize) const;
|
||||
|
|
@ -613,6 +628,10 @@ private:
|
|||
|
||||
String _get_prefix(Item *p_item, const Vector<int> &p_list_index, const Vector<ItemList *> &p_list_items);
|
||||
|
||||
static int _find_unquoted(const String &p_src, char32_t p_chr, int p_from);
|
||||
static Vector<String> _split_unquoted(const String &p_src, char32_t p_splitter);
|
||||
static String _get_tag_value(const String &p_tag);
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
// Kept for compatibility from 3.x to 4.0.
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
|
|
@ -621,6 +640,9 @@ private:
|
|||
String text;
|
||||
void _apply_translation();
|
||||
|
||||
bool internal_stack_editing = false;
|
||||
bool stack_externally_modified = false;
|
||||
|
||||
bool fit_content = false;
|
||||
|
||||
struct ThemeCache {
|
||||
|
|
@ -693,20 +715,20 @@ public:
|
|||
void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", TextServer::StructuredTextParser p_st_parser = TextServer::STRUCTURED_TEXT_DEFAULT, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE, const PackedFloat32Array &p_tab_stops = PackedFloat32Array());
|
||||
void push_indent(int p_level);
|
||||
void push_list(int p_level, ListType p_list, bool p_capitalize, const String &p_bullet = String::utf8("•"));
|
||||
void push_meta(const Variant &p_meta, MetaUnderline p_underline_mode = META_UNDERLINE_ALWAYS);
|
||||
void push_meta(const Variant &p_meta, MetaUnderline p_underline_mode = META_UNDERLINE_ALWAYS, const String &p_tooltip = String());
|
||||
void push_hint(const String &p_string);
|
||||
void push_table(int p_columns, InlineAlignment p_alignment = INLINE_ALIGNMENT_TOP, int p_align_to_row = -1);
|
||||
void push_fade(int p_start_index, int p_length);
|
||||
void push_shake(int p_strength, float p_rate, bool p_connected);
|
||||
void push_wave(float p_frequency, float p_amplitude, bool p_connected);
|
||||
void push_tornado(float p_frequency, float p_radius, bool p_connected);
|
||||
void push_rainbow(float p_saturation, float p_value, float p_frequency);
|
||||
void push_rainbow(float p_saturation, float p_value, float p_frequency, float p_speed);
|
||||
void push_pulse(const Color &p_color, float p_frequency, float p_ease);
|
||||
void push_bgcolor(const Color &p_color);
|
||||
void push_fgcolor(const Color &p_color);
|
||||
void push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment);
|
||||
void push_context();
|
||||
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1);
|
||||
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1, bool p_shrink = true);
|
||||
void set_cell_row_background_color(const Color &p_odd_row_bg, const Color &p_even_row_bg);
|
||||
void set_cell_border_color(const Color &p_color);
|
||||
void set_cell_size_override(const Size2 &p_min_size, const Size2 &p_max_size);
|
||||
|
|
@ -756,6 +778,7 @@ public:
|
|||
|
||||
void scroll_to_line(int p_line);
|
||||
int get_line_count() const;
|
||||
Vector2i get_line_range(int p_line);
|
||||
int get_visible_line_count() const;
|
||||
|
||||
int get_content_height() const;
|
||||
|
|
@ -772,10 +795,15 @@ public:
|
|||
bool is_selection_enabled() const;
|
||||
int get_selection_from() const;
|
||||
int get_selection_to() const;
|
||||
float get_selection_line_offset() const;
|
||||
String get_selected_text() const;
|
||||
void select_all();
|
||||
void selection_copy();
|
||||
|
||||
_FORCE_INLINE_ void set_selection_modifier(const Callable &p_modifier) {
|
||||
selection_modifier = p_modifier;
|
||||
}
|
||||
|
||||
void set_deselect_on_focus_loss_enabled(const bool p_enabled);
|
||||
bool is_deselect_on_focus_loss_enabled() const;
|
||||
|
||||
|
|
@ -785,8 +813,9 @@ public:
|
|||
void deselect();
|
||||
|
||||
int get_pending_paragraphs() const;
|
||||
bool is_ready() const;
|
||||
bool is_finished() const;
|
||||
bool is_updating() const;
|
||||
void wait_until_finished();
|
||||
|
||||
void set_threaded(bool p_threaded);
|
||||
bool is_threaded() const;
|
||||
|
|
@ -808,6 +837,18 @@ public:
|
|||
void set_text(const String &p_bbcode);
|
||||
String get_text() const;
|
||||
|
||||
void set_horizontal_alignment(HorizontalAlignment p_alignment);
|
||||
HorizontalAlignment get_horizontal_alignment() const;
|
||||
|
||||
void set_vertical_alignment(VerticalAlignment p_alignment);
|
||||
VerticalAlignment get_vertical_alignment() const;
|
||||
|
||||
void set_justification_flags(BitField<TextServer::JustificationFlag> p_flags);
|
||||
BitField<TextServer::JustificationFlag> get_justification_flags() const;
|
||||
|
||||
void set_tab_stops(const PackedFloat32Array &p_tab_stops);
|
||||
PackedFloat32Array get_tab_stops() const;
|
||||
|
||||
void set_text_direction(TextDirection p_text_direction);
|
||||
TextDirection get_text_direction() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@
|
|||
|
||||
#include "scroll_bar.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
|
|
@ -93,7 +90,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
|
|||
return;
|
||||
}
|
||||
|
||||
ofs -= decr_size;
|
||||
ofs -= decr_size + theme_cache.scroll_style->get_margin(orientation == VERTICAL ? SIDE_TOP : SIDE_LEFT);
|
||||
|
||||
if (ofs < grabber_ofs) {
|
||||
if (scrolling) {
|
||||
|
|
@ -151,7 +148,7 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
|
|||
Ref<Texture2D> decr = theme_cache.decrement_icon;
|
||||
|
||||
double decr_size = orientation == VERTICAL ? decr->get_height() : decr->get_width();
|
||||
ofs -= decr_size;
|
||||
ofs -= decr_size + theme_cache.scroll_style->get_margin(orientation == VERTICAL ? SIDE_TOP : SIDE_LEFT);
|
||||
|
||||
double diff = (ofs - drag.pos_at_click) / get_area_size();
|
||||
|
||||
|
|
@ -248,8 +245,6 @@ void ScrollBar::_notification(int p_what) {
|
|||
incr = theme_cache.increment_icon;
|
||||
}
|
||||
|
||||
Ref<StyleBox> bg = has_focus() ? theme_cache.scroll_focus_style : theme_cache.scroll_style;
|
||||
|
||||
Ref<StyleBox> grabber;
|
||||
if (drag.active) {
|
||||
grabber = theme_cache.grabber_pressed_style;
|
||||
|
|
@ -277,7 +272,11 @@ void ScrollBar::_notification(int p_what) {
|
|||
area.height -= incr->get_height() + decr->get_height();
|
||||
}
|
||||
|
||||
bg->draw(ci, Rect2(ofs, area));
|
||||
if (has_focus()) {
|
||||
theme_cache.scroll_focus_style->draw(ci, Rect2(ofs, area));
|
||||
} else {
|
||||
theme_cache.scroll_style->draw(ci, Rect2(ofs, area));
|
||||
}
|
||||
|
||||
if (orientation == HORIZONTAL) {
|
||||
ofs.width += area.width;
|
||||
|
|
@ -292,11 +291,11 @@ void ScrollBar::_notification(int p_what) {
|
|||
grabber_rect.size.width = get_grabber_size();
|
||||
grabber_rect.size.height = get_size().height;
|
||||
grabber_rect.position.y = 0;
|
||||
grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT);
|
||||
grabber_rect.position.x = get_grabber_offset() + decr->get_width() + theme_cache.scroll_style->get_margin(SIDE_LEFT);
|
||||
} else {
|
||||
grabber_rect.size.width = get_size().width;
|
||||
grabber_rect.size.height = get_grabber_size();
|
||||
grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP);
|
||||
grabber_rect.position.y = get_grabber_offset() + decr->get_height() + theme_cache.scroll_style->get_margin(SIDE_TOP);
|
||||
grabber_rect.position.x = 0;
|
||||
}
|
||||
|
||||
|
|
@ -472,7 +471,7 @@ double ScrollBar::get_area_size() const {
|
|||
}
|
||||
|
||||
double ScrollBar::get_grabber_offset() const {
|
||||
return (get_area_size()) * get_as_ratio();
|
||||
return get_area_size() * get_as_ratio();
|
||||
}
|
||||
|
||||
Size2 ScrollBar::get_minimum_size() const {
|
||||
|
|
|
|||
|
|
@ -35,14 +35,12 @@
|
|||
#include "scene/theme/theme_db.h"
|
||||
|
||||
Size2 ScrollContainer::get_minimum_size() const {
|
||||
Size2 min_size;
|
||||
|
||||
// Calculated in this function, as it needs to traverse all child controls once to calculate;
|
||||
// and needs to be calculated before being used by update_scrollbars().
|
||||
largest_child_min_size = Size2();
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisbilityMode::VISIBLE);
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -55,21 +53,23 @@ Size2 ScrollContainer::get_minimum_size() const {
|
|||
largest_child_min_size = largest_child_min_size.max(child_min_size);
|
||||
}
|
||||
|
||||
Size2 min_size;
|
||||
const Size2 size = get_size();
|
||||
|
||||
if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) {
|
||||
min_size.x = MAX(min_size.x, largest_child_min_size.x);
|
||||
min_size.x = largest_child_min_size.x;
|
||||
bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || vertical_scroll_mode == SCROLL_MODE_RESERVE || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > size.y);
|
||||
if (v_scroll_show && v_scroll->get_parent() == this) {
|
||||
min_size.x += v_scroll->get_minimum_size().x;
|
||||
}
|
||||
}
|
||||
|
||||
if (vertical_scroll_mode == SCROLL_MODE_DISABLED) {
|
||||
min_size.y = MAX(min_size.y, largest_child_min_size.y);
|
||||
}
|
||||
|
||||
bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > min_size.x);
|
||||
bool v_scroll_show = vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.y > min_size.y);
|
||||
|
||||
if (h_scroll_show && h_scroll->get_parent() == this) {
|
||||
min_size.y += h_scroll->get_minimum_size().y;
|
||||
}
|
||||
if (v_scroll_show && v_scroll->get_parent() == this) {
|
||||
min_size.x += v_scroll->get_minimum_size().x;
|
||||
min_size.y = largest_child_min_size.y;
|
||||
bool h_scroll_show = horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || horizontal_scroll_mode == SCROLL_MODE_RESERVE || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.x > size.x);
|
||||
if (h_scroll_show && h_scroll->get_parent() == this) {
|
||||
min_size.y += h_scroll->get_minimum_size().y;
|
||||
}
|
||||
}
|
||||
|
||||
min_size += theme_cache.panel_style->get_minimum_size();
|
||||
|
|
@ -92,6 +92,15 @@ void ScrollContainer::_cancel_drag() {
|
|||
}
|
||||
}
|
||||
|
||||
bool ScrollContainer::_is_h_scroll_visible() const {
|
||||
// Scrolls may have been moved out for reasons.
|
||||
return h_scroll->is_visible() && h_scroll->get_parent() == this;
|
||||
}
|
||||
|
||||
bool ScrollContainer::_is_v_scroll_visible() const {
|
||||
return v_scroll->is_visible() && v_scroll->get_parent() == this;
|
||||
}
|
||||
|
||||
void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
ERR_FAIL_COND(p_gui_input.is_null());
|
||||
|
||||
|
|
@ -272,21 +281,31 @@ void ScrollContainer::_gui_focus_changed(Control *p_control) {
|
|||
if (follow_focus && is_ancestor_of(p_control)) {
|
||||
ensure_control_visible(p_control);
|
||||
}
|
||||
if (draw_focus_border) {
|
||||
const bool _should_draw_focus_border = has_focus() || child_has_focus();
|
||||
if (focus_border_is_drawn != _should_draw_focus_border) {
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollContainer::ensure_control_visible(Control *p_control) {
|
||||
ERR_FAIL_COND_MSG(!is_ancestor_of(p_control), "Must be an ancestor of the control.");
|
||||
|
||||
Rect2 global_rect = get_global_rect();
|
||||
Rect2 other_rect = p_control->get_global_rect();
|
||||
// Just eliminate the rotation of this ScrollContainer.
|
||||
Transform2D other_in_this = get_global_transform().affine_inverse() * p_control->get_global_transform();
|
||||
|
||||
Size2 size = get_size();
|
||||
Rect2 other_rect = other_in_this.xform(Rect2(Point2(), p_control->get_size()));
|
||||
|
||||
float side_margin = v_scroll->is_visible() ? v_scroll->get_size().x : 0.0f;
|
||||
float bottom_margin = h_scroll->is_visible() ? h_scroll->get_size().y : 0.0f;
|
||||
|
||||
Vector2 diff = Vector2(MAX(MIN(other_rect.position.x - (is_layout_rtl() ? side_margin : 0.0f), global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x + (!is_layout_rtl() ? side_margin : 0.0f)),
|
||||
MAX(MIN(other_rect.position.y, global_rect.position.y), other_rect.position.y + other_rect.size.y - global_rect.size.y + bottom_margin));
|
||||
Vector2 diff = Vector2(MAX(MIN(other_rect.position.x - (is_layout_rtl() ? side_margin : 0.0f), 0.0f), other_rect.position.x + other_rect.size.x - size.x + (!is_layout_rtl() ? side_margin : 0.0f)),
|
||||
MAX(MIN(other_rect.position.y, 0.0f), other_rect.position.y + other_rect.size.y - size.y + bottom_margin));
|
||||
|
||||
set_h_scroll(get_h_scroll() + (diff.x - global_rect.position.x));
|
||||
set_v_scroll(get_v_scroll() + (diff.y - global_rect.position.y));
|
||||
set_h_scroll(get_h_scroll() + diff.x);
|
||||
set_v_scroll(get_v_scroll() + diff.y);
|
||||
}
|
||||
|
||||
void ScrollContainer::_reposition_children() {
|
||||
|
|
@ -298,11 +317,11 @@ void ScrollContainer::_reposition_children() {
|
|||
ofs += theme_cache.panel_style->get_offset();
|
||||
bool rtl = is_layout_rtl();
|
||||
|
||||
if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) { //scrolls may have been moved out for reasons
|
||||
if (_is_h_scroll_visible() || horizontal_scroll_mode == SCROLL_MODE_RESERVE) {
|
||||
size.y -= h_scroll->get_minimum_size().y;
|
||||
}
|
||||
|
||||
if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { //scrolls may have been moved out for reasons
|
||||
if (_is_v_scroll_visible() || vertical_scroll_mode == SCROLL_MODE_RESERVE) {
|
||||
size.x -= v_scroll->get_minimum_size().x;
|
||||
}
|
||||
|
||||
|
|
@ -324,7 +343,7 @@ void ScrollContainer::_reposition_children() {
|
|||
r.size.height = MAX(size.height, minsize.height);
|
||||
}
|
||||
r.position += ofs;
|
||||
if (rtl && v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) {
|
||||
if (rtl && _is_v_scroll_visible()) {
|
||||
r.position.x += v_scroll->get_minimum_size().x;
|
||||
}
|
||||
r.position = r.position.floor();
|
||||
|
|
@ -357,6 +376,15 @@ void ScrollContainer::_notification(int p_what) {
|
|||
|
||||
case NOTIFICATION_DRAW: {
|
||||
draw_style_box(theme_cache.panel_style, Rect2(Vector2(), get_size()));
|
||||
if (draw_focus_border && (has_focus() || child_has_focus())) {
|
||||
RID ci = get_canvas_item();
|
||||
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
|
||||
draw_style_box(theme_cache.focus_style, Rect2(Point2(), get_size()));
|
||||
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
|
||||
focus_border_is_drawn = true;
|
||||
} else {
|
||||
focus_border_is_drawn = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
|
|
@ -436,14 +464,14 @@ void ScrollContainer::update_scrollbars() {
|
|||
Size2 hmin = h_scroll->get_combined_minimum_size();
|
||||
Size2 vmin = v_scroll->get_combined_minimum_size();
|
||||
|
||||
h_scroll->set_visible(horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (horizontal_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.width > size.width));
|
||||
v_scroll->set_visible(vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || (vertical_scroll_mode == SCROLL_MODE_AUTO && largest_child_min_size.height > size.height));
|
||||
h_scroll->set_visible(horizontal_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || ((horizontal_scroll_mode == SCROLL_MODE_AUTO || horizontal_scroll_mode == SCROLL_MODE_RESERVE) && largest_child_min_size.width > size.width));
|
||||
v_scroll->set_visible(vertical_scroll_mode == SCROLL_MODE_SHOW_ALWAYS || ((vertical_scroll_mode == SCROLL_MODE_AUTO || vertical_scroll_mode == SCROLL_MODE_RESERVE) && largest_child_min_size.height > size.height));
|
||||
|
||||
h_scroll->set_max(largest_child_min_size.width);
|
||||
h_scroll->set_page((v_scroll->is_visible() && v_scroll->get_parent() == this) ? size.width - vmin.width : size.width);
|
||||
h_scroll->set_page(_is_v_scroll_visible() ? size.width - vmin.width : size.width);
|
||||
|
||||
v_scroll->set_max(largest_child_min_size.height);
|
||||
v_scroll->set_page((h_scroll->is_visible() && h_scroll->get_parent() == this) ? size.height - hmin.height : size.height);
|
||||
v_scroll->set_page(_is_h_scroll_visible() ? size.height - hmin.height : size.height);
|
||||
|
||||
// Avoid scrollbar overlapping.
|
||||
_updating_scrollbars = true;
|
||||
|
|
@ -452,7 +480,7 @@ void ScrollContainer::update_scrollbars() {
|
|||
|
||||
void ScrollContainer::_scroll_moved(float) {
|
||||
queue_sort();
|
||||
};
|
||||
}
|
||||
|
||||
void ScrollContainer::set_h_scroll(int p_pos) {
|
||||
h_scroll->set_value(p_pos);
|
||||
|
|
@ -538,7 +566,7 @@ PackedStringArray ScrollContainer::get_configuration_warnings() const {
|
|||
int found = 0;
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i));
|
||||
Control *c = as_sortable_control(get_child(i), SortableVisibilityMode::VISIBLE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -593,29 +621,52 @@ void ScrollContainer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &ScrollContainer::get_v_scroll_bar);
|
||||
ClassDB::bind_method(D_METHOD("ensure_control_visible", "control"), &ScrollContainer::ensure_control_visible);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_draw_focus_border", "draw"), &ScrollContainer::set_draw_focus_border);
|
||||
ClassDB::bind_method(D_METHOD("get_draw_focus_border"), &ScrollContainer::get_draw_focus_border);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("scroll_started"));
|
||||
ADD_SIGNAL(MethodInfo("scroll_ended"));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_focus"), "set_follow_focus", "is_following_focus");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_focus_border"), "set_draw_focus_border", "get_draw_focus_border");
|
||||
|
||||
ADD_GROUP("Scroll", "scroll_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal", PROPERTY_HINT_NONE, "suffix:px"), "set_h_scroll", "get_h_scroll");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical", PROPERTY_HINT_NONE, "suffix:px"), "set_v_scroll", "get_v_scroll");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_horizontal_custom_step", PROPERTY_HINT_RANGE, "-1,4096,suffix:px"), "set_horizontal_custom_step", "get_horizontal_custom_step");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical_custom_step", PROPERTY_HINT_RANGE, "-1,4096,suffix:px"), "set_vertical_custom_step", "get_vertical_custom_step");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_horizontal_scroll_mode", "get_horizontal_scroll_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_vertical_scroll_mode", "get_vertical_scroll_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show,Reserve"), "set_horizontal_scroll_mode", "get_horizontal_scroll_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show,Reserve"), "set_vertical_scroll_mode", "get_vertical_scroll_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
|
||||
|
||||
BIND_ENUM_CONSTANT(SCROLL_MODE_DISABLED);
|
||||
BIND_ENUM_CONSTANT(SCROLL_MODE_AUTO);
|
||||
BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_ALWAYS);
|
||||
BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_NEVER);
|
||||
BIND_ENUM_CONSTANT(SCROLL_MODE_RESERVE);
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollContainer, panel_style, "panel");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollContainer, focus_style, "focus");
|
||||
|
||||
GLOBAL_DEF("gui/common/default_scroll_deadzone", 0);
|
||||
};
|
||||
}
|
||||
|
||||
void ScrollContainer::set_draw_focus_border(bool p_draw) {
|
||||
if (draw_focus_border == p_draw) {
|
||||
return;
|
||||
}
|
||||
draw_focus_border = p_draw;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
bool ScrollContainer::get_draw_focus_border() {
|
||||
return draw_focus_border;
|
||||
}
|
||||
|
||||
bool ScrollContainer::child_has_focus() {
|
||||
const Control *focus_owner = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr;
|
||||
return focus_owner && is_ancestor_of(focus_owner);
|
||||
}
|
||||
|
||||
ScrollContainer::ScrollContainer() {
|
||||
h_scroll = memnew(HScrollBar);
|
||||
|
|
@ -631,4 +682,4 @@ ScrollContainer::ScrollContainer() {
|
|||
deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");
|
||||
|
||||
set_clip_contents(true);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ public:
|
|||
SCROLL_MODE_AUTO,
|
||||
SCROLL_MODE_SHOW_ALWAYS,
|
||||
SCROLL_MODE_SHOW_NEVER,
|
||||
SCROLL_MODE_RESERVE,
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
@ -71,10 +72,17 @@ private:
|
|||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> panel_style;
|
||||
Ref<StyleBox> focus_style;
|
||||
} theme_cache;
|
||||
|
||||
void _cancel_drag();
|
||||
|
||||
bool _is_h_scroll_visible() const;
|
||||
bool _is_v_scroll_visible() const;
|
||||
|
||||
bool draw_focus_border = false;
|
||||
bool focus_border_is_drawn = false;
|
||||
|
||||
protected:
|
||||
Size2 get_minimum_size() const override;
|
||||
|
||||
|
|
@ -90,6 +98,7 @@ protected:
|
|||
|
||||
public:
|
||||
virtual void gui_input(const Ref<InputEvent> &p_gui_input) override;
|
||||
bool child_has_focus();
|
||||
|
||||
void set_h_scroll(int p_pos);
|
||||
int get_h_scroll() const;
|
||||
|
|
@ -121,6 +130,9 @@ public:
|
|||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
void set_draw_focus_border(bool p_draw);
|
||||
bool get_draw_focus_border();
|
||||
|
||||
ScrollContainer();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "slider.h"
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
Size2 Slider::get_minimum_size() const {
|
||||
|
|
@ -74,7 +73,8 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (orientation == VERTICAL) {
|
||||
set_as_ratio(1 - (((double)grab.pos - (grab_height / 2.0)) / max));
|
||||
} else {
|
||||
set_as_ratio(((double)grab.pos - (grab_width / 2.0)) / max);
|
||||
double v = ((double)grab.pos - (grab_width / 2.0)) / max;
|
||||
set_as_ratio(is_layout_rtl() ? 1 - v : v);
|
||||
}
|
||||
set_block_signals(false);
|
||||
grab.active = true;
|
||||
|
|
@ -113,6 +113,8 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
|
|||
double motion = (orientation == VERTICAL ? mm->get_position().y : mm->get_position().x) - grab.pos;
|
||||
if (orientation == VERTICAL) {
|
||||
motion = -motion;
|
||||
} else if (is_layout_rtl()) {
|
||||
motion = -motion;
|
||||
}
|
||||
double areasize = orientation == VERTICAL ? size.height - grab_height : size.width - grab_width;
|
||||
if (areasize <= 0) {
|
||||
|
|
@ -128,7 +130,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
|
|||
Ref<InputEventJoypadButton> joypadbutton_event = p_event;
|
||||
bool is_joypad_event = (joypadmotion_event.is_valid() || joypadbutton_event.is_valid());
|
||||
|
||||
if (!mm.is_valid() && !mb.is_valid()) {
|
||||
if (mm.is_null() && mb.is_null()) {
|
||||
if (p_event->is_action_pressed("ui_left", true)) {
|
||||
if (orientation != HORIZONTAL) {
|
||||
return;
|
||||
|
|
@ -139,7 +141,11 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
set_process_internal(true);
|
||||
}
|
||||
set_value(get_value() - (custom_step >= 0 ? custom_step : get_step()));
|
||||
if (is_layout_rtl()) {
|
||||
set_value(get_value() + (custom_step >= 0 ? custom_step : get_step()));
|
||||
} else {
|
||||
set_value(get_value() - (custom_step >= 0 ? custom_step : get_step()));
|
||||
}
|
||||
accept_event();
|
||||
} else if (p_event->is_action_pressed("ui_right", true)) {
|
||||
if (orientation != HORIZONTAL) {
|
||||
|
|
@ -151,7 +157,11 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
set_process_internal(true);
|
||||
}
|
||||
set_value(get_value() + (custom_step >= 0 ? custom_step : get_step()));
|
||||
if (is_layout_rtl()) {
|
||||
set_value(get_value() - (custom_step >= 0 ? custom_step : get_step()));
|
||||
} else {
|
||||
set_value(get_value() + (custom_step >= 0 ? custom_step : get_step()));
|
||||
}
|
||||
accept_event();
|
||||
} else if (p_event->is_action_pressed("ui_up", true)) {
|
||||
if (orientation != VERTICAL) {
|
||||
|
|
@ -203,11 +213,19 @@ void Slider::_notification(int p_what) {
|
|||
gamepad_event_delay_ms = GAMEPAD_EVENT_REPEAT_RATE_MS + gamepad_event_delay_ms;
|
||||
if (orientation == HORIZONTAL) {
|
||||
if (input->is_action_pressed("ui_left")) {
|
||||
set_value(get_value() - (custom_step >= 0 ? custom_step : get_step()));
|
||||
if (is_layout_rtl()) {
|
||||
set_value(get_value() + (custom_step >= 0 ? custom_step : get_step()));
|
||||
} else {
|
||||
set_value(get_value() - (custom_step >= 0 ? custom_step : get_step()));
|
||||
}
|
||||
}
|
||||
|
||||
if (input->is_action_pressed("ui_right")) {
|
||||
set_value(get_value() + (custom_step >= 0 ? custom_step : get_step()));
|
||||
if (is_layout_rtl()) {
|
||||
set_value(get_value() - (custom_step >= 0 ? custom_step : get_step()));
|
||||
} else {
|
||||
set_value(get_value() + (custom_step >= 0 ? custom_step : get_step()));
|
||||
}
|
||||
}
|
||||
} else if (orientation == VERTICAL) {
|
||||
if (input->is_action_pressed("ui_down")) {
|
||||
|
|
@ -275,7 +293,7 @@ void Slider::_notification(int p_what) {
|
|||
double areasize = size.height - (theme_cache.center_grabber ? 0 : grabber->get_height());
|
||||
int grabber_shift = theme_cache.center_grabber ? grabber->get_height() / 2 : 0;
|
||||
style->draw(ci, Rect2i(Point2i(size.width / 2 - widget_width / 2, 0), Size2i(widget_width, size.height)));
|
||||
grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_height() / 2 + grabber_shift), Size2i(widget_width, areasize * ratio + grabber->get_height() / 2 - grabber_shift)));
|
||||
grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, Math::round(size.height - areasize * ratio - grabber->get_height() / 2 + grabber_shift)), Size2i(widget_width, Math::round(areasize * ratio + grabber->get_height() / 2 - grabber_shift))));
|
||||
|
||||
if (ticks > 1) {
|
||||
int grabber_offset = (grabber->get_height() / 2 - tick->get_height() / 2);
|
||||
|
|
@ -292,9 +310,15 @@ void Slider::_notification(int p_what) {
|
|||
int widget_height = style->get_minimum_size().height;
|
||||
double areasize = size.width - (theme_cache.center_grabber ? 0 : grabber->get_size().width);
|
||||
int grabber_shift = theme_cache.center_grabber ? -grabber->get_width() / 2 : 0;
|
||||
bool rtl = is_layout_rtl();
|
||||
|
||||
style->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(size.width, widget_height)));
|
||||
grabber_area->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(areasize * ratio + grabber->get_width() / 2 + grabber_shift, widget_height)));
|
||||
int p = areasize * (rtl ? 1 - ratio : ratio) + grabber->get_width() / 2 + grabber_shift;
|
||||
if (rtl) {
|
||||
grabber_area->draw(ci, Rect2i(Point2i(p, (size.height - widget_height) / 2), Size2i(size.width - p, widget_height)));
|
||||
} else {
|
||||
grabber_area->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(p, widget_height)));
|
||||
}
|
||||
|
||||
if (ticks > 1) {
|
||||
int grabber_offset = (grabber->get_width() / 2 - tick->get_width() / 2);
|
||||
|
|
@ -306,7 +330,7 @@ void Slider::_notification(int p_what) {
|
|||
tick->draw(ci, Point2i(ofs, (size.height - widget_height) / 2));
|
||||
}
|
||||
}
|
||||
grabber->draw(ci, Point2i(ratio * areasize + grabber_shift, size.height / 2 - grabber->get_height() / 2 + theme_cache.grabber_offset));
|
||||
grabber->draw(ci, Point2i((rtl ? 1 - ratio : ratio) * areasize + grabber_shift, size.height / 2 - grabber->get_height() / 2 + theme_cache.grabber_offset));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,17 +36,26 @@
|
|||
|
||||
Size2 SpinBox::get_minimum_size() const {
|
||||
Size2 ms = line_edit->get_combined_minimum_size();
|
||||
ms.width += last_w;
|
||||
ms.width += sizing_cache.buttons_block_width;
|
||||
return ms;
|
||||
}
|
||||
|
||||
void SpinBox::_update_text(bool p_keep_line_edit) {
|
||||
String value = String::num(get_value(), Math::range_step_decimals(get_step()));
|
||||
void SpinBox::_update_text(bool p_only_update_if_value_changed) {
|
||||
double step = get_step();
|
||||
if (use_custom_arrow_step && custom_arrow_step != 0.0) {
|
||||
step = custom_arrow_step;
|
||||
}
|
||||
String value = String::num(get_value(), Math::range_step_decimals(step));
|
||||
if (is_localizing_numeral_system()) {
|
||||
value = TS->format_number(value);
|
||||
}
|
||||
|
||||
if (!line_edit->has_focus()) {
|
||||
if (p_only_update_if_value_changed && value == last_text_value) {
|
||||
return;
|
||||
}
|
||||
last_text_value = value;
|
||||
|
||||
if (!line_edit->is_editing()) {
|
||||
if (!prefix.is_empty()) {
|
||||
value = prefix + " " + value;
|
||||
}
|
||||
|
|
@ -54,16 +63,15 @@ void SpinBox::_update_text(bool p_keep_line_edit) {
|
|||
value += " " + suffix;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_keep_line_edit && value == last_updated_text && value != line_edit->get_text()) {
|
||||
return;
|
||||
}
|
||||
|
||||
line_edit->set_text_with_selection(value);
|
||||
last_updated_text = value;
|
||||
}
|
||||
|
||||
void SpinBox::_text_submitted(const String &p_string) {
|
||||
if (p_string.is_empty()) {
|
||||
_update_text();
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<Expression> expr;
|
||||
expr.instantiate();
|
||||
|
||||
|
|
@ -75,6 +83,9 @@ void SpinBox::_text_submitted(const String &p_string) {
|
|||
text = text.trim_prefix(prefix + " ").trim_suffix(" " + suffix);
|
||||
|
||||
Error err = expr->parse(text);
|
||||
|
||||
use_custom_arrow_step = false;
|
||||
|
||||
if (err != OK) {
|
||||
// If the expression failed try without converting commas to dots - they might have been for parameter separation.
|
||||
text = p_string;
|
||||
|
|
@ -109,13 +120,21 @@ LineEdit *SpinBox::get_line_edit() {
|
|||
}
|
||||
|
||||
void SpinBox::_line_edit_input(const Ref<InputEvent> &p_event) {
|
||||
if (drag.enabled) {
|
||||
line_edit->accept_event();
|
||||
}
|
||||
}
|
||||
|
||||
void SpinBox::_range_click_timeout() {
|
||||
if (!drag.enabled && Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
|
||||
bool up = get_local_mouse_position().y < (get_size().height / 2);
|
||||
double step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
|
||||
set_value(get_value() + (up ? step : -step));
|
||||
double step = get_step();
|
||||
// Arrow button is being pressed, so we also need to set the step to the same value as custom_arrow_step if its not 0.
|
||||
double temp_step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
|
||||
_set_step_no_signal(temp_step);
|
||||
set_value(get_value() + (up ? temp_step : -temp_step));
|
||||
_set_step_no_signal(step);
|
||||
use_custom_arrow_step = true;
|
||||
|
||||
if (range_click_timer->is_one_shot()) {
|
||||
range_click_timer->set_wait_time(0.075);
|
||||
|
|
@ -128,7 +147,7 @@ void SpinBox::_range_click_timeout() {
|
|||
}
|
||||
}
|
||||
|
||||
void SpinBox::_release_mouse() {
|
||||
void SpinBox::_release_mouse_from_drag_mode() {
|
||||
if (drag.enabled) {
|
||||
drag.enabled = false;
|
||||
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_HIDDEN);
|
||||
|
|
@ -137,6 +156,14 @@ void SpinBox::_release_mouse() {
|
|||
}
|
||||
}
|
||||
|
||||
void SpinBox::_mouse_exited() {
|
||||
if (state_cache.up_button_hovered || state_cache.down_button_hovered) {
|
||||
state_cache.up_button_hovered = false;
|
||||
state_cache.down_button_hovered = false;
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
|
||||
ERR_FAIL_COND(p_event.is_null());
|
||||
|
||||
|
|
@ -144,18 +171,40 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
|
|||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouse> me = p_event;
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
|
||||
double step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
|
||||
double step = get_step();
|
||||
Vector2 mpos;
|
||||
bool mouse_on_up_button = false;
|
||||
bool mouse_on_down_button = false;
|
||||
if (mb.is_valid() || mm.is_valid()) {
|
||||
Rect2 up_button_rc = Rect2(sizing_cache.buttons_left, 0, sizing_cache.buttons_width, sizing_cache.button_up_height);
|
||||
Rect2 down_button_rc = Rect2(sizing_cache.buttons_left, sizing_cache.second_button_top, sizing_cache.buttons_width, sizing_cache.button_down_height);
|
||||
|
||||
mpos = me->get_position();
|
||||
|
||||
mouse_on_up_button = up_button_rc.has_point(mpos);
|
||||
mouse_on_down_button = down_button_rc.has_point(mpos);
|
||||
}
|
||||
|
||||
if (mb.is_valid() && mb->is_pressed()) {
|
||||
bool up = mb->get_position().y < (get_size().height / 2);
|
||||
|
||||
switch (mb->get_button_index()) {
|
||||
case MouseButton::LEFT: {
|
||||
line_edit->grab_focus();
|
||||
|
||||
set_value(get_value() + (up ? step : -step));
|
||||
if (mouse_on_up_button || mouse_on_down_button) {
|
||||
// Arrow button is being pressed, so step is being changed temporarily.
|
||||
double temp_step = get_custom_arrow_step() != 0.0 ? get_custom_arrow_step() : get_step();
|
||||
_set_step_no_signal(temp_step);
|
||||
set_value(get_value() + (mouse_on_up_button ? temp_step : -temp_step));
|
||||
_set_step_no_signal(step);
|
||||
use_custom_arrow_step = true;
|
||||
}
|
||||
state_cache.up_button_pressed = mouse_on_up_button;
|
||||
state_cache.down_button_pressed = mouse_on_down_button;
|
||||
queue_redraw();
|
||||
|
||||
range_click_timer->set_wait_time(0.6);
|
||||
range_click_timer->set_one_shot(true);
|
||||
|
|
@ -166,16 +215,21 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
|
|||
} break;
|
||||
case MouseButton::RIGHT: {
|
||||
line_edit->grab_focus();
|
||||
set_value((up ? get_max() : get_min()));
|
||||
if (mouse_on_up_button || mouse_on_down_button) {
|
||||
use_custom_arrow_step = false;
|
||||
set_value(mouse_on_up_button ? get_max() : get_min());
|
||||
}
|
||||
} break;
|
||||
case MouseButton::WHEEL_UP: {
|
||||
if (line_edit->has_focus()) {
|
||||
if (line_edit->is_editing()) {
|
||||
use_custom_arrow_step = false;
|
||||
set_value(get_value() + step * mb->get_factor());
|
||||
accept_event();
|
||||
}
|
||||
} break;
|
||||
case MouseButton::WHEEL_DOWN: {
|
||||
if (line_edit->has_focus()) {
|
||||
if (line_edit->is_editing()) {
|
||||
use_custom_arrow_step = false;
|
||||
set_value(get_value() - step * mb->get_factor());
|
||||
accept_event();
|
||||
}
|
||||
|
|
@ -186,19 +240,36 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
|
||||
if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (state_cache.up_button_pressed || state_cache.down_button_pressed) {
|
||||
state_cache.up_button_pressed = false;
|
||||
state_cache.down_button_pressed = false;
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
//set_default_cursor_shape(CURSOR_ARROW);
|
||||
range_click_timer->stop();
|
||||
_release_mouse();
|
||||
_release_mouse_from_drag_mode();
|
||||
drag.allowed = false;
|
||||
line_edit->clear_pending_select_all_on_focus();
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
bool old_up_hovered = state_cache.up_button_hovered;
|
||||
bool old_down_hovered = state_cache.down_button_hovered;
|
||||
|
||||
state_cache.up_button_hovered = mouse_on_up_button;
|
||||
state_cache.down_button_hovered = mouse_on_down_button;
|
||||
|
||||
if (old_up_hovered != state_cache.up_button_hovered || old_down_hovered != state_cache.down_button_hovered) {
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
|
||||
if (drag.enabled) {
|
||||
drag.diff_y += mm->get_relative().y;
|
||||
double diff_y = -0.01 * Math::pow(ABS(drag.diff_y), 1.8) * SIGN(drag.diff_y);
|
||||
use_custom_arrow_step = false;
|
||||
set_value(CLAMP(drag.base_val + step * diff_y, get_min(), get_max()));
|
||||
} else if (drag.allowed && drag.capture_pos.distance_to(mm->get_position()) > 2) {
|
||||
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
|
||||
|
|
@ -209,71 +280,156 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
|
||||
void SpinBox::_line_edit_focus_enter() {
|
||||
int col = line_edit->get_caret_column();
|
||||
_update_text();
|
||||
line_edit->set_caret_column(col);
|
||||
|
||||
// LineEdit text might change and it clears any selection. Have to re-select here.
|
||||
if (line_edit->is_select_all_on_focus() && !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
|
||||
line_edit->select_all();
|
||||
}
|
||||
}
|
||||
|
||||
void SpinBox::_line_edit_focus_exit() {
|
||||
// Discontinue because the focus_exit was caused by left-clicking the arrows.
|
||||
const Viewport *viewport = get_viewport();
|
||||
if (!viewport || viewport->gui_get_focus_owner() == get_line_edit()) {
|
||||
return;
|
||||
}
|
||||
// Discontinue because the focus_exit was caused by right-click context menu.
|
||||
if (line_edit->is_menu_visible()) {
|
||||
return;
|
||||
}
|
||||
// Discontinue because the focus_exit was caused by canceling.
|
||||
if (Input::get_singleton()->is_action_pressed("ui_cancel")) {
|
||||
void SpinBox::_line_edit_editing_toggled(bool p_toggled_on) {
|
||||
if (p_toggled_on) {
|
||||
int col = line_edit->get_caret_column();
|
||||
_update_text();
|
||||
return;
|
||||
}
|
||||
line_edit->set_caret_column(col);
|
||||
|
||||
_text_submitted(line_edit->get_text());
|
||||
// LineEdit text might change and it clears any selection. Have to re-select here.
|
||||
if (line_edit->is_select_all_on_focus() && !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) {
|
||||
line_edit->select_all();
|
||||
}
|
||||
} else {
|
||||
if (Input::get_singleton()->is_action_pressed("ui_cancel") || line_edit->get_text().is_empty()) {
|
||||
_update_text(); // Revert text if editing was canceled.
|
||||
} else {
|
||||
_update_text(true); // Update text in case value was changed this frame (e.g. on `focus_exited`).
|
||||
_text_submitted(line_edit->get_text());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) {
|
||||
int w = icon->get_width();
|
||||
if ((w != last_w)) {
|
||||
inline void SpinBox::_compute_sizes() {
|
||||
int buttons_block_wanted_width = theme_cache.buttons_width + theme_cache.field_and_buttons_separation;
|
||||
int buttons_block_icon_enforced_width = _get_widest_button_icon_width() + theme_cache.field_and_buttons_separation;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
const bool min_width_from_icons = theme_cache.set_min_buttons_width_from_icons || (theme_cache.buttons_width < 0);
|
||||
#else
|
||||
const bool min_width_from_icons = theme_cache.buttons_width < 0;
|
||||
#endif
|
||||
int w = min_width_from_icons != 0 ? MAX(buttons_block_icon_enforced_width, buttons_block_wanted_width) : buttons_block_wanted_width;
|
||||
|
||||
if (w != sizing_cache.buttons_block_width) {
|
||||
line_edit->set_offset(SIDE_LEFT, 0);
|
||||
line_edit->set_offset(SIDE_RIGHT, -w);
|
||||
last_w = w;
|
||||
sizing_cache.buttons_block_width = w;
|
||||
}
|
||||
|
||||
Size2i size = get_size();
|
||||
|
||||
sizing_cache.buttons_width = w - theme_cache.field_and_buttons_separation;
|
||||
sizing_cache.buttons_vertical_separation = CLAMP(theme_cache.buttons_vertical_separation, 0, size.height);
|
||||
sizing_cache.buttons_left = is_layout_rtl() ? 0 : size.width - sizing_cache.buttons_width;
|
||||
sizing_cache.button_up_height = (size.height - sizing_cache.buttons_vertical_separation) / 2;
|
||||
sizing_cache.button_down_height = size.height - sizing_cache.button_up_height - sizing_cache.buttons_vertical_separation;
|
||||
sizing_cache.second_button_top = size.height - sizing_cache.button_down_height;
|
||||
|
||||
sizing_cache.buttons_separator_top = sizing_cache.button_up_height;
|
||||
sizing_cache.field_and_buttons_separator_left = is_layout_rtl() ? sizing_cache.buttons_width : size.width - sizing_cache.buttons_block_width;
|
||||
sizing_cache.field_and_buttons_separator_width = theme_cache.field_and_buttons_separation;
|
||||
}
|
||||
|
||||
inline int SpinBox::_get_widest_button_icon_width() {
|
||||
int max = 0;
|
||||
max = MAX(max, theme_cache.updown_icon->get_width());
|
||||
max = MAX(max, theme_cache.up_icon->get_width());
|
||||
max = MAX(max, theme_cache.up_hover_icon->get_width());
|
||||
max = MAX(max, theme_cache.up_pressed_icon->get_width());
|
||||
max = MAX(max, theme_cache.up_disabled_icon->get_width());
|
||||
max = MAX(max, theme_cache.down_icon->get_width());
|
||||
max = MAX(max, theme_cache.down_hover_icon->get_width());
|
||||
max = MAX(max, theme_cache.down_pressed_icon->get_width());
|
||||
max = MAX(max, theme_cache.down_disabled_icon->get_width());
|
||||
return max;
|
||||
}
|
||||
|
||||
void SpinBox::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_DRAW: {
|
||||
_update_text(true);
|
||||
_adjust_width_for_icon(theme_cache.updown_icon);
|
||||
_compute_sizes();
|
||||
|
||||
RID ci = get_canvas_item();
|
||||
Size2i size = get_size();
|
||||
|
||||
if (is_layout_rtl()) {
|
||||
theme_cache.updown_icon->draw(ci, Point2i(0, (size.height - theme_cache.updown_icon->get_height()) / 2));
|
||||
} else {
|
||||
theme_cache.updown_icon->draw(ci, Point2i(size.width - theme_cache.updown_icon->get_width(), (size.height - theme_cache.updown_icon->get_height()) / 2));
|
||||
Ref<StyleBox> up_stylebox = theme_cache.up_base_stylebox;
|
||||
Ref<StyleBox> down_stylebox = theme_cache.down_base_stylebox;
|
||||
Ref<Texture2D> up_icon = theme_cache.up_icon;
|
||||
Ref<Texture2D> down_icon = theme_cache.down_icon;
|
||||
Color up_icon_modulate = theme_cache.up_icon_modulate;
|
||||
Color down_icon_modulate = theme_cache.down_icon_modulate;
|
||||
|
||||
bool is_fully_disabled = !is_editable();
|
||||
|
||||
if (state_cache.up_button_disabled || is_fully_disabled) {
|
||||
up_stylebox = theme_cache.up_disabled_stylebox;
|
||||
up_icon = theme_cache.up_disabled_icon;
|
||||
up_icon_modulate = theme_cache.up_disabled_icon_modulate;
|
||||
} else if (state_cache.up_button_pressed && !drag.enabled) {
|
||||
up_stylebox = theme_cache.up_pressed_stylebox;
|
||||
up_icon = theme_cache.up_pressed_icon;
|
||||
up_icon_modulate = theme_cache.up_pressed_icon_modulate;
|
||||
} else if (state_cache.up_button_hovered && !drag.enabled) {
|
||||
up_stylebox = theme_cache.up_hover_stylebox;
|
||||
up_icon = theme_cache.up_hover_icon;
|
||||
up_icon_modulate = theme_cache.up_hover_icon_modulate;
|
||||
}
|
||||
|
||||
if (state_cache.down_button_disabled || is_fully_disabled) {
|
||||
down_stylebox = theme_cache.down_disabled_stylebox;
|
||||
down_icon = theme_cache.down_disabled_icon;
|
||||
down_icon_modulate = theme_cache.down_disabled_icon_modulate;
|
||||
} else if (state_cache.down_button_pressed && !drag.enabled) {
|
||||
down_stylebox = theme_cache.down_pressed_stylebox;
|
||||
down_icon = theme_cache.down_pressed_icon;
|
||||
down_icon_modulate = theme_cache.down_pressed_icon_modulate;
|
||||
} else if (state_cache.down_button_hovered && !drag.enabled) {
|
||||
down_stylebox = theme_cache.down_hover_stylebox;
|
||||
down_icon = theme_cache.down_hover_icon;
|
||||
down_icon_modulate = theme_cache.down_hover_icon_modulate;
|
||||
}
|
||||
|
||||
int updown_icon_left = sizing_cache.buttons_left + (sizing_cache.buttons_width - theme_cache.updown_icon->get_width()) / 2;
|
||||
int updown_icon_top = (size.height - theme_cache.updown_icon->get_height()) / 2;
|
||||
|
||||
// Compute center icon positions once we know which one is used.
|
||||
int up_icon_left = sizing_cache.buttons_left + (sizing_cache.buttons_width - up_icon->get_width()) / 2;
|
||||
int up_icon_top = (sizing_cache.button_up_height - up_icon->get_height()) / 2;
|
||||
int down_icon_left = sizing_cache.buttons_left + (sizing_cache.buttons_width - down_icon->get_width()) / 2;
|
||||
int down_icon_top = sizing_cache.second_button_top + (sizing_cache.button_down_height - down_icon->get_height()) / 2;
|
||||
|
||||
// Draw separators.
|
||||
draw_style_box(theme_cache.up_down_buttons_separator, Rect2(sizing_cache.buttons_left, sizing_cache.buttons_separator_top, sizing_cache.buttons_width, sizing_cache.buttons_vertical_separation));
|
||||
draw_style_box(theme_cache.field_and_buttons_separator, Rect2(sizing_cache.field_and_buttons_separator_left, 0, sizing_cache.field_and_buttons_separator_width, size.height));
|
||||
|
||||
// Draw buttons.
|
||||
draw_style_box(up_stylebox, Rect2(sizing_cache.buttons_left, 0, sizing_cache.buttons_width, sizing_cache.button_up_height));
|
||||
draw_style_box(down_stylebox, Rect2(sizing_cache.buttons_left, sizing_cache.second_button_top, sizing_cache.buttons_width, sizing_cache.button_down_height));
|
||||
|
||||
// Draw arrows.
|
||||
theme_cache.updown_icon->draw(ci, Point2i(updown_icon_left, updown_icon_top));
|
||||
draw_texture(up_icon, Point2i(up_icon_left, up_icon_top), up_icon_modulate);
|
||||
draw_texture(down_icon, Point2i(down_icon_left, down_icon_top), down_icon_modulate);
|
||||
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_MOUSE_EXIT: {
|
||||
_mouse_exited();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
_adjust_width_for_icon(theme_cache.updown_icon);
|
||||
_compute_sizes();
|
||||
_update_text();
|
||||
_update_buttons_state_for_current_value();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED:
|
||||
drag.allowed = false;
|
||||
[[fallthrough]];
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
_release_mouse();
|
||||
_release_mouse_from_drag_mode();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
|
|
@ -353,6 +509,7 @@ bool SpinBox::is_select_all_on_focus() const {
|
|||
|
||||
void SpinBox::set_editable(bool p_enabled) {
|
||||
line_edit->set_editable(p_enabled);
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
bool SpinBox::is_editable() const {
|
||||
|
|
@ -371,6 +528,34 @@ double SpinBox::get_custom_arrow_step() const {
|
|||
return custom_arrow_step;
|
||||
}
|
||||
|
||||
void SpinBox::_value_changed(double p_value) {
|
||||
_update_buttons_state_for_current_value();
|
||||
}
|
||||
|
||||
void SpinBox::_update_buttons_state_for_current_value() {
|
||||
double value = get_value();
|
||||
bool should_disable_up = value == get_max() && !is_greater_allowed();
|
||||
bool should_disable_down = value == get_min() && !is_lesser_allowed();
|
||||
|
||||
if (state_cache.up_button_disabled != should_disable_up || state_cache.down_button_disabled != should_disable_down) {
|
||||
state_cache.up_button_disabled = should_disable_up;
|
||||
state_cache.down_button_disabled = should_disable_down;
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void SpinBox::_set_step_no_signal(double p_step) {
|
||||
set_block_signals(true);
|
||||
set_step(p_step);
|
||||
set_block_signals(false);
|
||||
}
|
||||
|
||||
void SpinBox::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "exp_edit") {
|
||||
p_property.usage = PROPERTY_USAGE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void SpinBox::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &SpinBox::set_horizontal_alignment);
|
||||
ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &SpinBox::get_horizontal_alignment);
|
||||
|
|
@ -397,20 +582,57 @@ void SpinBox::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_arrow_step", PROPERTY_HINT_RANGE, "0,10000,0.0001,or_greater"), "set_custom_arrow_step", "get_custom_arrow_step");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus");
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, buttons_vertical_separation);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, field_and_buttons_separation);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, buttons_width);
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, set_min_buttons_width_from_icons);
|
||||
#endif
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, updown_icon, "updown");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, up_icon, "up");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, up_hover_icon, "up_hover");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, up_pressed_icon, "up_pressed");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, up_disabled_icon, "up_disabled");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, down_icon, "down");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, down_hover_icon, "down_hover");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, down_pressed_icon, "down_pressed");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SpinBox, down_disabled_icon, "down_disabled");
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_base_stylebox, "up_background");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_hover_stylebox, "up_background_hovered");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_pressed_stylebox, "up_background_pressed");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_disabled_stylebox, "up_background_disabled");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, down_base_stylebox, "down_background");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, down_hover_stylebox, "down_background_hovered");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, down_pressed_stylebox, "down_background_pressed");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, down_disabled_stylebox, "down_background_disabled");
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, up_icon_modulate, "up_icon_modulate");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, up_hover_icon_modulate, "up_hover_icon_modulate");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, up_pressed_icon_modulate, "up_pressed_icon_modulate");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, up_disabled_icon_modulate, "up_disabled_icon_modulate");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, down_icon_modulate, "down_icon_modulate");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, down_hover_icon_modulate, "down_hover_icon_modulate");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, down_pressed_icon_modulate, "down_pressed_icon_modulate");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, SpinBox, down_disabled_icon_modulate, "down_disabled_icon_modulate");
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, field_and_buttons_separator, "field_and_buttons_separator");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SpinBox, up_down_buttons_separator, "up_down_buttons_separator");
|
||||
}
|
||||
|
||||
SpinBox::SpinBox() {
|
||||
line_edit = memnew(LineEdit);
|
||||
line_edit->set_emoji_menu_enabled(false);
|
||||
add_child(line_edit, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
line_edit->set_theme_type_variation("SpinBoxInnerLineEdit");
|
||||
line_edit->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
|
||||
line_edit->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
|
||||
|
||||
line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), CONNECT_DEFERRED);
|
||||
line_edit->connect(SceneStringName(focus_entered), callable_mp(this, &SpinBox::_line_edit_focus_enter), CONNECT_DEFERRED);
|
||||
line_edit->connect(SceneStringName(focus_exited), callable_mp(this, &SpinBox::_line_edit_focus_exit), CONNECT_DEFERRED);
|
||||
line_edit->connect(SceneStringName(text_submitted), callable_mp(this, &SpinBox::_text_submitted), CONNECT_DEFERRED);
|
||||
line_edit->connect("editing_toggled", callable_mp(this, &SpinBox::_line_edit_editing_toggled), CONNECT_DEFERRED);
|
||||
line_edit->connect(SceneStringName(gui_input), callable_mp(this, &SpinBox::_line_edit_input));
|
||||
|
||||
range_click_timer = memnew(Timer);
|
||||
|
|
|
|||
|
|
@ -39,21 +39,34 @@ class SpinBox : public Range {
|
|||
GDCLASS(SpinBox, Range);
|
||||
|
||||
LineEdit *line_edit = nullptr;
|
||||
int last_w = 0;
|
||||
bool update_on_text_changed = false;
|
||||
|
||||
struct SizingCache {
|
||||
int buttons_block_width = 0;
|
||||
int buttons_width = 0;
|
||||
int buttons_vertical_separation = 0;
|
||||
int buttons_left = 0;
|
||||
int button_up_height = 0;
|
||||
int button_down_height = 0;
|
||||
int second_button_top = 0;
|
||||
int buttons_separator_top = 0;
|
||||
int field_and_buttons_separator_left = 0;
|
||||
int field_and_buttons_separator_width = 0;
|
||||
} sizing_cache;
|
||||
|
||||
Timer *range_click_timer = nullptr;
|
||||
void _range_click_timeout();
|
||||
void _release_mouse();
|
||||
void _release_mouse_from_drag_mode();
|
||||
|
||||
void _update_text(bool p_keep_line_edit = false);
|
||||
void _update_text(bool p_only_update_if_value_changed = false);
|
||||
void _text_submitted(const String &p_string);
|
||||
void _text_changed(const String &p_string);
|
||||
|
||||
String prefix;
|
||||
String suffix;
|
||||
String last_updated_text;
|
||||
String last_text_value;
|
||||
double custom_arrow_step = 0.0;
|
||||
bool use_custom_arrow_step = false;
|
||||
|
||||
void _line_edit_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
|
|
@ -65,17 +78,68 @@ class SpinBox : public Range {
|
|||
double diff_y = 0.0;
|
||||
} drag;
|
||||
|
||||
void _line_edit_focus_enter();
|
||||
void _line_edit_focus_exit();
|
||||
struct StateCache {
|
||||
bool up_button_hovered = false;
|
||||
bool up_button_pressed = false;
|
||||
bool up_button_disabled = false;
|
||||
bool down_button_hovered = false;
|
||||
bool down_button_pressed = false;
|
||||
bool down_button_disabled = false;
|
||||
} state_cache;
|
||||
|
||||
inline void _adjust_width_for_icon(const Ref<Texture2D> &icon);
|
||||
void _line_edit_editing_toggled(bool p_toggled_on);
|
||||
|
||||
inline void _compute_sizes();
|
||||
inline int _get_widest_button_icon_width();
|
||||
|
||||
struct ThemeCache {
|
||||
Ref<Texture2D> updown_icon;
|
||||
Ref<Texture2D> up_icon;
|
||||
Ref<Texture2D> up_hover_icon;
|
||||
Ref<Texture2D> up_pressed_icon;
|
||||
Ref<Texture2D> up_disabled_icon;
|
||||
Ref<Texture2D> down_icon;
|
||||
Ref<Texture2D> down_hover_icon;
|
||||
Ref<Texture2D> down_pressed_icon;
|
||||
Ref<Texture2D> down_disabled_icon;
|
||||
|
||||
Ref<StyleBox> up_base_stylebox;
|
||||
Ref<StyleBox> up_hover_stylebox;
|
||||
Ref<StyleBox> up_pressed_stylebox;
|
||||
Ref<StyleBox> up_disabled_stylebox;
|
||||
Ref<StyleBox> down_base_stylebox;
|
||||
Ref<StyleBox> down_hover_stylebox;
|
||||
Ref<StyleBox> down_pressed_stylebox;
|
||||
Ref<StyleBox> down_disabled_stylebox;
|
||||
|
||||
Color up_icon_modulate;
|
||||
Color up_hover_icon_modulate;
|
||||
Color up_pressed_icon_modulate;
|
||||
Color up_disabled_icon_modulate;
|
||||
Color down_icon_modulate;
|
||||
Color down_hover_icon_modulate;
|
||||
Color down_pressed_icon_modulate;
|
||||
Color down_disabled_icon_modulate;
|
||||
|
||||
Ref<StyleBox> field_and_buttons_separator;
|
||||
Ref<StyleBox> up_down_buttons_separator;
|
||||
|
||||
int buttons_vertical_separation = 0;
|
||||
int field_and_buttons_separation = 0;
|
||||
int buttons_width = 0;
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
bool set_min_buttons_width_from_icons = false;
|
||||
#endif
|
||||
} theme_cache;
|
||||
|
||||
void _mouse_exited();
|
||||
void _update_buttons_state_for_current_value();
|
||||
void _set_step_no_signal(double p_step);
|
||||
|
||||
protected:
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
void _value_changed(double p_value) override;
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@
|
|||
|
||||
#include "split_container.h"
|
||||
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/margin_container.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
|
||||
|
|
@ -39,7 +38,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
|
||||
|
||||
if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || sc->dragger_visibility != SplitContainer::DRAGGER_VISIBLE) {
|
||||
if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || !sc->dragging_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -48,8 +47,9 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (mb.is_valid()) {
|
||||
if (mb->get_button_index() == MouseButton::LEFT) {
|
||||
if (mb->is_pressed()) {
|
||||
sc->_compute_middle_sep(true);
|
||||
sc->_compute_split_offset(true);
|
||||
dragging = true;
|
||||
sc->emit_signal(SNAME("drag_started"));
|
||||
drag_ofs = sc->split_offset;
|
||||
if (sc->vertical) {
|
||||
drag_from = get_transform().xform(mb->get_position()).y;
|
||||
|
|
@ -59,6 +59,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
|
|||
} else {
|
||||
dragging = false;
|
||||
queue_redraw();
|
||||
sc->emit_signal(SNAME("drag_ended"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -76,7 +77,7 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
|
|||
} else {
|
||||
sc->split_offset = drag_ofs + ((sc->vertical ? in_parent_pos.y : in_parent_pos.x) - drag_from);
|
||||
}
|
||||
sc->_compute_middle_sep(true);
|
||||
sc->_compute_split_offset(true);
|
||||
sc->queue_sort();
|
||||
sc->emit_signal(SNAME("dragged"), sc->get_split_offset());
|
||||
}
|
||||
|
|
@ -84,11 +85,9 @@ void SplitContainerDragger::gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
Control::CursorShape SplitContainerDragger::get_cursor_shape(const Point2 &p_pos) const {
|
||||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
|
||||
|
||||
if (!sc->collapsed && sc->dragger_visibility == SplitContainer::DRAGGER_VISIBLE) {
|
||||
if (!sc->collapsed && sc->dragging_enabled) {
|
||||
return (sc->vertical ? CURSOR_VSPLIT : CURSOR_HSPLIT);
|
||||
}
|
||||
|
||||
return Control::get_cursor_shape(p_pos);
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +100,6 @@ void SplitContainerDragger::_notification(int p_what) {
|
|||
queue_redraw();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_MOUSE_EXIT: {
|
||||
mouse_inside = false;
|
||||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
|
||||
|
|
@ -109,22 +107,25 @@ void SplitContainerDragger::_notification(int p_what) {
|
|||
queue_redraw();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
|
||||
if (!dragging && !mouse_inside && sc->theme_cache.autohide) {
|
||||
return;
|
||||
draw_style_box(sc->theme_cache.split_bar_background, split_bar_rect);
|
||||
if (sc->dragger_visibility == sc->DRAGGER_VISIBLE && (dragging || mouse_inside || !sc->theme_cache.autohide)) {
|
||||
Ref<Texture2D> tex = sc->_get_grabber_icon();
|
||||
float available_size = sc->vertical ? (sc->get_size().x - tex->get_size().x) : (sc->get_size().y - tex->get_size().y);
|
||||
if (available_size - sc->drag_area_margin_begin - sc->drag_area_margin_end > 0) { // Draw the grabber only if it fits.
|
||||
draw_texture(tex, (split_bar_rect.get_position() + (split_bar_rect.get_size() - tex->get_size()) * 0.5));
|
||||
}
|
||||
}
|
||||
if (sc->show_drag_area && Engine::get_singleton()->is_editor_hint()) {
|
||||
draw_rect(Rect2(Vector2(0, 0), get_size()), sc->dragging_enabled ? Color(1, 1, 0, 0.3) : Color(1, 0, 0, 0.3));
|
||||
}
|
||||
|
||||
Ref<Texture2D> tex = sc->_get_grabber_icon();
|
||||
draw_texture(tex, (get_size() - tex->get_size()) / 2);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode) const {
|
||||
Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisibilityMode p_visibility_mode) const {
|
||||
int idx = 0;
|
||||
|
||||
for (int i = 0; i < get_child_count(false); i++) {
|
||||
Control *c = as_sortable_control(get_child(i, false), p_visibility_mode);
|
||||
if (!c) {
|
||||
|
|
@ -137,7 +138,6 @@ Control *SplitContainer::_get_sortable_child(int p_idx, SortableVisbilityMode p_
|
|||
|
||||
idx++;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -153,45 +153,48 @@ Ref<Texture2D> SplitContainer::_get_grabber_icon() const {
|
|||
}
|
||||
}
|
||||
|
||||
void SplitContainer::_compute_middle_sep(bool p_clamp) {
|
||||
int SplitContainer::_get_separation() const {
|
||||
if (dragger_visibility == DRAGGER_HIDDEN_COLLAPSED) {
|
||||
return 0;
|
||||
}
|
||||
// DRAGGER_VISIBLE or DRAGGER_HIDDEN.
|
||||
Ref<Texture2D> g = _get_grabber_icon();
|
||||
return MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width());
|
||||
}
|
||||
|
||||
void SplitContainer::_compute_split_offset(bool p_clamp) {
|
||||
Control *first = _get_sortable_child(0);
|
||||
Control *second = _get_sortable_child(1);
|
||||
int axis_index = vertical ? 1 : 0;
|
||||
int size = get_size()[axis_index];
|
||||
int sep = _get_separation();
|
||||
|
||||
// Determine expanded children.
|
||||
bool first_expanded = (vertical ? first->get_v_size_flags() : first->get_h_size_flags()) & SIZE_EXPAND;
|
||||
bool second_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND;
|
||||
|
||||
// Compute the minimum size.
|
||||
int axis = vertical ? 1 : 0;
|
||||
int size = get_size()[axis];
|
||||
int ms_first = first->get_combined_minimum_size()[axis];
|
||||
int ms_second = second->get_combined_minimum_size()[axis];
|
||||
|
||||
// Determine the separation between items.
|
||||
Ref<Texture2D> g = _get_grabber_icon();
|
||||
int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
|
||||
|
||||
// Compute the wished separation_point.
|
||||
int wished_middle_sep = 0;
|
||||
// Compute the wished size.
|
||||
int wished_size = 0;
|
||||
int split_offset_with_collapse = 0;
|
||||
if (!collapsed) {
|
||||
split_offset_with_collapse = split_offset;
|
||||
}
|
||||
if (first_expanded && second_expanded) {
|
||||
bool first_is_expanded = (vertical ? first->get_v_size_flags() : first->get_h_size_flags()) & SIZE_EXPAND;
|
||||
bool second_is_expanded = (vertical ? second->get_v_size_flags() : second->get_h_size_flags()) & SIZE_EXPAND;
|
||||
|
||||
if (first_is_expanded && second_is_expanded) {
|
||||
float ratio = first->get_stretch_ratio() / (first->get_stretch_ratio() + second->get_stretch_ratio());
|
||||
wished_middle_sep = size * ratio - sep / 2 + split_offset_with_collapse;
|
||||
} else if (first_expanded) {
|
||||
wished_middle_sep = size - sep + split_offset_with_collapse;
|
||||
wished_size = size * ratio - sep * 0.5 + split_offset_with_collapse;
|
||||
} else if (first_is_expanded) {
|
||||
wished_size = size - sep + split_offset_with_collapse;
|
||||
} else {
|
||||
wished_middle_sep = split_offset_with_collapse;
|
||||
wished_size = split_offset_with_collapse;
|
||||
}
|
||||
|
||||
// Clamp the middle sep to acceptatble values.
|
||||
middle_sep = CLAMP(wished_middle_sep, ms_first, size - sep - ms_second);
|
||||
// Clamp the split offset to acceptable values.
|
||||
int first_min_size = first->get_combined_minimum_size()[axis_index];
|
||||
int second_min_size = second->get_combined_minimum_size()[axis_index];
|
||||
computed_split_offset = CLAMP(wished_size, first_min_size, size - sep - second_min_size);
|
||||
|
||||
// Clamp the split_offset if requested.
|
||||
if (p_clamp) {
|
||||
split_offset -= wished_middle_sep - middle_sep;
|
||||
split_offset -= wished_size - computed_split_offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -199,8 +202,7 @@ void SplitContainer::_resort() {
|
|||
Control *first = _get_sortable_child(0);
|
||||
Control *second = _get_sortable_child(1);
|
||||
|
||||
// If we have only one element.
|
||||
if (!first || !second) {
|
||||
if (!first || !second) { // Only one child.
|
||||
if (first) {
|
||||
fit_child_in_rect(first, Rect2(Point2(), get_size()));
|
||||
} else if (second) {
|
||||
|
|
@ -209,56 +211,53 @@ void SplitContainer::_resort() {
|
|||
dragging_area_control->hide();
|
||||
return;
|
||||
}
|
||||
dragging_area_control->set_visible(!collapsed);
|
||||
|
||||
// If we have more that one.
|
||||
_compute_middle_sep(false);
|
||||
_compute_split_offset(false); // This recalculates and sets computed_split_offset.
|
||||
|
||||
// Determine the separation between items.
|
||||
Ref<Texture2D> g = _get_grabber_icon();
|
||||
int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
|
||||
int sep = _get_separation();
|
||||
bool is_rtl = is_layout_rtl();
|
||||
|
||||
// Move the children, including the dragger.
|
||||
// Move the children.
|
||||
if (vertical) {
|
||||
fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(get_size().width, middle_sep)));
|
||||
int sofs = middle_sep + sep;
|
||||
fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(get_size().width, computed_split_offset)));
|
||||
int sofs = computed_split_offset + sep;
|
||||
fit_child_in_rect(second, Rect2(Point2(0, sofs), Size2(get_size().width, get_size().height - sofs)));
|
||||
} else {
|
||||
if (is_layout_rtl()) {
|
||||
middle_sep = get_size().width - middle_sep - sep;
|
||||
fit_child_in_rect(second, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height)));
|
||||
int sofs = middle_sep + sep;
|
||||
if (is_rtl) {
|
||||
computed_split_offset = get_size().width - computed_split_offset - sep;
|
||||
fit_child_in_rect(second, Rect2(Point2(0, 0), Size2(computed_split_offset, get_size().height)));
|
||||
int sofs = computed_split_offset + sep;
|
||||
fit_child_in_rect(first, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height)));
|
||||
} else {
|
||||
fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(middle_sep, get_size().height)));
|
||||
int sofs = middle_sep + sep;
|
||||
fit_child_in_rect(first, Rect2(Point2(0, 0), Size2(computed_split_offset, get_size().height)));
|
||||
int sofs = computed_split_offset + sep;
|
||||
fit_child_in_rect(second, Rect2(Point2(sofs, 0), Size2(get_size().width - sofs, get_size().height)));
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the dragger visibility and position.
|
||||
if (dragger_visibility == DRAGGER_VISIBLE && !collapsed) {
|
||||
dragging_area_control->show();
|
||||
|
||||
int dragger_ctrl_size = MAX(sep, theme_cache.minimum_grab_thickness);
|
||||
if (vertical) {
|
||||
dragging_area_control->set_rect(Rect2(Point2(0, middle_sep - (dragger_ctrl_size - sep) / 2), Size2(get_size().width, dragger_ctrl_size)));
|
||||
} else {
|
||||
dragging_area_control->set_rect(Rect2(Point2(middle_sep - (dragger_ctrl_size - sep) / 2, 0), Size2(dragger_ctrl_size, get_size().height)));
|
||||
}
|
||||
|
||||
dragging_area_control->queue_redraw();
|
||||
dragging_area_control->set_mouse_filter(dragging_enabled ? MOUSE_FILTER_STOP : MOUSE_FILTER_IGNORE);
|
||||
const int dragger_ctrl_size = MAX(sep, theme_cache.minimum_grab_thickness);
|
||||
float split_bar_offset = (dragger_ctrl_size - sep) * 0.5;
|
||||
if (vertical) {
|
||||
Rect2 split_bar_rect = Rect2(is_rtl ? drag_area_margin_end : drag_area_margin_begin, computed_split_offset, get_size().width - drag_area_margin_begin - drag_area_margin_end, sep);
|
||||
dragging_area_control->set_rect(Rect2(split_bar_rect.position.x, split_bar_rect.position.y - split_bar_offset + drag_area_offset, split_bar_rect.size.x, dragger_ctrl_size));
|
||||
dragging_area_control->split_bar_rect = Rect2(Vector2(0.0, int(split_bar_offset) - drag_area_offset), split_bar_rect.size);
|
||||
} else {
|
||||
dragging_area_control->hide();
|
||||
Rect2 split_bar_rect = Rect2(computed_split_offset, drag_area_margin_begin, sep, get_size().height - drag_area_margin_begin - drag_area_margin_end);
|
||||
dragging_area_control->set_rect(Rect2(split_bar_rect.position.x - split_bar_offset + drag_area_offset * (is_rtl ? -1 : 1), split_bar_rect.position.y, dragger_ctrl_size, split_bar_rect.size.y));
|
||||
dragging_area_control->split_bar_rect = Rect2(Vector2(int(split_bar_offset) - drag_area_offset * (is_rtl ? -1 : 1), 0.0), split_bar_rect.size);
|
||||
}
|
||||
queue_redraw();
|
||||
dragging_area_control->queue_redraw();
|
||||
}
|
||||
|
||||
Size2 SplitContainer::get_minimum_size() const {
|
||||
Size2i minimum;
|
||||
Ref<Texture2D> g = _get_grabber_icon();
|
||||
int sep = (dragger_visibility != DRAGGER_HIDDEN_COLLAPSED) ? MAX(theme_cache.separation, vertical ? g->get_height() : g->get_width()) : 0;
|
||||
int sep = _get_separation();
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Control *child = _get_sortable_child(i, SortableVisbilityMode::VISIBLE);
|
||||
Control *child = _get_sortable_child(i, SortableVisibilityMode::VISIBLE);
|
||||
if (!child) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -297,11 +296,9 @@ void SplitContainer::_notification(int p_what) {
|
|||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
|
||||
queue_sort();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_SORT_CHILDREN: {
|
||||
_resort();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
update_minimum_size();
|
||||
} break;
|
||||
|
|
@ -312,9 +309,7 @@ void SplitContainer::set_split_offset(int p_offset) {
|
|||
if (split_offset == p_offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
split_offset = p_offset;
|
||||
|
||||
queue_sort();
|
||||
}
|
||||
|
||||
|
|
@ -326,8 +321,7 @@ void SplitContainer::clamp_split_offset() {
|
|||
if (!_get_sortable_child(0) || !_get_sortable_child(1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_compute_middle_sep(true);
|
||||
_compute_split_offset(true);
|
||||
queue_sort();
|
||||
}
|
||||
|
||||
|
|
@ -335,7 +329,6 @@ void SplitContainer::set_collapsed(bool p_collapsed) {
|
|||
if (collapsed == p_collapsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
collapsed = p_collapsed;
|
||||
queue_sort();
|
||||
}
|
||||
|
|
@ -344,7 +337,6 @@ void SplitContainer::set_dragger_visibility(DraggerVisibility p_visibility) {
|
|||
if (dragger_visibility == p_visibility) {
|
||||
return;
|
||||
}
|
||||
|
||||
dragger_visibility = p_visibility;
|
||||
queue_sort();
|
||||
}
|
||||
|
|
@ -368,6 +360,26 @@ bool SplitContainer::is_vertical() const {
|
|||
return vertical;
|
||||
}
|
||||
|
||||
void SplitContainer::set_dragging_enabled(bool p_enabled) {
|
||||
if (dragging_enabled == p_enabled) {
|
||||
return;
|
||||
}
|
||||
dragging_enabled = p_enabled;
|
||||
if (!dragging_enabled && dragging_area_control->dragging) {
|
||||
dragging_area_control->dragging = false;
|
||||
// queue_redraw() is called by _resort().
|
||||
emit_signal(SNAME("drag_ended"));
|
||||
}
|
||||
if (get_viewport()) {
|
||||
get_viewport()->update_mouse_cursor_state();
|
||||
}
|
||||
_resort();
|
||||
}
|
||||
|
||||
bool SplitContainer::is_dragging_enabled() const {
|
||||
return dragging_enabled;
|
||||
}
|
||||
|
||||
Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const {
|
||||
Vector<int> flags;
|
||||
flags.append(SIZE_FILL);
|
||||
|
|
@ -392,6 +404,51 @@ Vector<int> SplitContainer::get_allowed_size_flags_vertical() const {
|
|||
return flags;
|
||||
}
|
||||
|
||||
void SplitContainer::set_drag_area_margin_begin(int p_margin) {
|
||||
if (drag_area_margin_begin == p_margin) {
|
||||
return;
|
||||
}
|
||||
drag_area_margin_begin = p_margin;
|
||||
queue_sort();
|
||||
}
|
||||
|
||||
int SplitContainer::get_drag_area_margin_begin() const {
|
||||
return drag_area_margin_begin;
|
||||
}
|
||||
|
||||
void SplitContainer::set_drag_area_margin_end(int p_margin) {
|
||||
if (drag_area_margin_end == p_margin) {
|
||||
return;
|
||||
}
|
||||
drag_area_margin_end = p_margin;
|
||||
queue_sort();
|
||||
}
|
||||
|
||||
int SplitContainer::get_drag_area_margin_end() const {
|
||||
return drag_area_margin_end;
|
||||
}
|
||||
|
||||
void SplitContainer::set_drag_area_offset(int p_offset) {
|
||||
if (drag_area_offset == p_offset) {
|
||||
return;
|
||||
}
|
||||
drag_area_offset = p_offset;
|
||||
queue_sort();
|
||||
}
|
||||
|
||||
int SplitContainer::get_drag_area_offset() const {
|
||||
return drag_area_offset;
|
||||
}
|
||||
|
||||
void SplitContainer::set_show_drag_area_enabled(bool p_enabled) {
|
||||
show_drag_area = p_enabled;
|
||||
dragging_area_control->queue_redraw();
|
||||
}
|
||||
|
||||
bool SplitContainer::is_show_drag_area_enabled() const {
|
||||
return show_drag_area;
|
||||
}
|
||||
|
||||
void SplitContainer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_split_offset", "offset"), &SplitContainer::set_split_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_split_offset"), &SplitContainer::get_split_offset);
|
||||
|
|
@ -406,13 +463,39 @@ void SplitContainer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &SplitContainer::set_vertical);
|
||||
ClassDB::bind_method(D_METHOD("is_vertical"), &SplitContainer::is_vertical);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_dragging_enabled", "dragging_enabled"), &SplitContainer::set_dragging_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_dragging_enabled"), &SplitContainer::is_dragging_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_drag_area_margin_begin", "margin"), &SplitContainer::set_drag_area_margin_begin);
|
||||
ClassDB::bind_method(D_METHOD("get_drag_area_margin_begin"), &SplitContainer::get_drag_area_margin_begin);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_drag_area_margin_end", "margin"), &SplitContainer::set_drag_area_margin_end);
|
||||
ClassDB::bind_method(D_METHOD("get_drag_area_margin_end"), &SplitContainer::get_drag_area_margin_end);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_drag_area_offset", "offset"), &SplitContainer::set_drag_area_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_drag_area_offset"), &SplitContainer::get_drag_area_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_drag_area_highlight_in_editor", "drag_area_highlight_in_editor"), &SplitContainer::set_show_drag_area_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_drag_area_highlight_in_editor_enabled"), &SplitContainer::is_show_drag_area_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_drag_area_control"), &SplitContainer::get_drag_area_control);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::INT, "offset")));
|
||||
ADD_SIGNAL(MethodInfo("drag_started"));
|
||||
ADD_SIGNAL(MethodInfo("drag_ended"));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "split_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_split_offset", "get_split_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dragging_enabled"), "set_dragging_enabled", "is_dragging_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden and Collapsed"), "set_dragger_visibility", "get_dragger_visibility");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical");
|
||||
|
||||
ADD_GROUP("Drag Area", "drag_area_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_margin_begin", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_margin_begin", "get_drag_area_margin_begin");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_margin_end", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_margin_end", "get_drag_area_margin_end");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "drag_area_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_drag_area_offset", "get_drag_area_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_area_highlight_in_editor"), "set_drag_area_highlight_in_editor", "is_drag_area_highlight_in_editor_enabled");
|
||||
|
||||
BIND_ENUM_CONSTANT(DRAGGER_VISIBLE);
|
||||
BIND_ENUM_CONSTANT(DRAGGER_HIDDEN);
|
||||
BIND_ENUM_CONSTANT(DRAGGER_HIDDEN_COLLAPSED);
|
||||
|
|
@ -423,6 +506,7 @@ void SplitContainer::_bind_methods() {
|
|||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon, "grabber");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon_h, "h_grabber");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_ICON, SplitContainer, grabber_icon_v, "v_grabber");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, SplitContainer, split_bar_background, "split_bar_background");
|
||||
}
|
||||
|
||||
SplitContainer::SplitContainer(bool p_vertical) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
|
||||
class SplitContainerDragger : public Control {
|
||||
GDCLASS(SplitContainerDragger, Control);
|
||||
friend class SplitContainer;
|
||||
Rect2 split_bar_rect;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
|
@ -62,11 +64,16 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
int show_drag_area = false;
|
||||
int drag_area_margin_begin = 0;
|
||||
int drag_area_margin_end = 0;
|
||||
int drag_area_offset = 0;
|
||||
int split_offset = 0;
|
||||
int middle_sep = 0;
|
||||
int computed_split_offset = 0;
|
||||
bool vertical = false;
|
||||
bool collapsed = false;
|
||||
DraggerVisibility dragger_visibility = DRAGGER_VISIBLE;
|
||||
bool dragging_enabled = true;
|
||||
|
||||
SplitContainerDragger *dragging_area_control = nullptr;
|
||||
|
||||
|
|
@ -77,12 +84,15 @@ private:
|
|||
Ref<Texture2D> grabber_icon;
|
||||
Ref<Texture2D> grabber_icon_h;
|
||||
Ref<Texture2D> grabber_icon_v;
|
||||
float base_scale = 1.0;
|
||||
Ref<StyleBox> split_bar_background;
|
||||
} theme_cache;
|
||||
|
||||
Ref<Texture2D> _get_grabber_icon() const;
|
||||
void _compute_middle_sep(bool p_clamp);
|
||||
void _compute_split_offset(bool p_clamp);
|
||||
int _get_separation() const;
|
||||
void _resort();
|
||||
Control *_get_sortable_child(int p_idx, SortableVisbilityMode p_visibility_mode = SortableVisbilityMode::VISIBLE_IN_TREE) const;
|
||||
Control *_get_sortable_child(int p_idx, SortableVisibilityMode p_visibility_mode = SortableVisibilityMode::VISIBLE_IN_TREE) const;
|
||||
|
||||
protected:
|
||||
bool is_fixed = false;
|
||||
|
|
@ -105,11 +115,28 @@ public:
|
|||
void set_vertical(bool p_vertical);
|
||||
bool is_vertical() const;
|
||||
|
||||
void set_dragging_enabled(bool p_enabled);
|
||||
bool is_dragging_enabled() const;
|
||||
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
|
||||
virtual Vector<int> get_allowed_size_flags_vertical() const override;
|
||||
|
||||
void set_drag_area_margin_begin(int p_margin);
|
||||
int get_drag_area_margin_begin() const;
|
||||
|
||||
void set_drag_area_margin_end(int p_margin);
|
||||
int get_drag_area_margin_end() const;
|
||||
|
||||
void set_drag_area_offset(int p_offset);
|
||||
int get_drag_area_offset() const;
|
||||
|
||||
void set_show_drag_area_enabled(bool p_enabled);
|
||||
bool is_show_drag_area_enabled() const;
|
||||
|
||||
Control *get_drag_area_control() { return dragging_area_control; }
|
||||
|
||||
SplitContainer(bool p_vertical = false);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -246,6 +246,14 @@ bool SubViewportContainer::_is_propagated_in_gui_input(const Ref<InputEvent> &p_
|
|||
return false;
|
||||
}
|
||||
|
||||
void SubViewportContainer::set_mouse_target(bool p_enable) {
|
||||
mouse_target = p_enable;
|
||||
}
|
||||
|
||||
bool SubViewportContainer::is_mouse_target_enabled() {
|
||||
return mouse_target;
|
||||
}
|
||||
|
||||
void SubViewportContainer::add_child_notify(Node *p_child) {
|
||||
if (Object::cast_to<SubViewport>(p_child)) {
|
||||
queue_redraw();
|
||||
|
|
@ -259,7 +267,7 @@ void SubViewportContainer::remove_child_notify(Node *p_child) {
|
|||
}
|
||||
|
||||
PackedStringArray SubViewportContainer::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Container::get_configuration_warnings();
|
||||
|
||||
bool has_viewport = false;
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
|
|
@ -286,8 +294,12 @@ void SubViewportContainer::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_stretch_shrink", "amount"), &SubViewportContainer::set_stretch_shrink);
|
||||
ClassDB::bind_method(D_METHOD("get_stretch_shrink"), &SubViewportContainer::get_stretch_shrink);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_mouse_target", "amount"), &SubViewportContainer::set_mouse_target);
|
||||
ClassDB::bind_method(D_METHOD("is_mouse_target_enabled"), &SubViewportContainer::is_mouse_target_enabled);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch"), "set_stretch", "is_stretch_enabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_shrink", PROPERTY_HINT_RANGE, "1,32,1,or_greater"), "set_stretch_shrink", "get_stretch_shrink");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "mouse_target"), "set_mouse_target", "is_mouse_target_enabled");
|
||||
|
||||
GDVIRTUAL_BIND(_propagate_input_event, "event");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ class SubViewportContainer : public Container {
|
|||
|
||||
bool stretch = false;
|
||||
int shrink = 1;
|
||||
bool mouse_target = false;
|
||||
|
||||
void _notify_viewports(int p_notification);
|
||||
bool _is_propagated_in_gui_input(const Ref<InputEvent> &p_event);
|
||||
void _send_event_to_viewports(const Ref<InputEvent> &p_event);
|
||||
|
|
@ -63,6 +65,9 @@ public:
|
|||
int get_stretch_shrink() const;
|
||||
void recalc_force_viewport_sizes();
|
||||
|
||||
void set_mouse_target(bool p_enable);
|
||||
bool is_mouse_target_enabled();
|
||||
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
virtual Vector<int> get_allowed_size_flags_horizontal() const override;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "tab_bar.h"
|
||||
|
||||
#include "core/string/translation.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/texture_rect.h"
|
||||
|
|
@ -354,6 +353,8 @@ void TabBar::_notification(int p_what) {
|
|||
if (scroll_to_selected) {
|
||||
ensure_tab_visible(current);
|
||||
}
|
||||
// Set initialized even if no tabs were set.
|
||||
initialized = true;
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
|
|
@ -655,10 +656,10 @@ void TabBar::set_tab_count(int p_count) {
|
|||
}
|
||||
|
||||
if (!initialized) {
|
||||
if (queued_current != current) {
|
||||
current = queued_current;
|
||||
}
|
||||
initialized = true;
|
||||
if (queued_current != CURRENT_TAB_UNINITIALIZED && queued_current != current) {
|
||||
set_current_tab(queued_current);
|
||||
}
|
||||
}
|
||||
|
||||
queue_redraw();
|
||||
|
|
@ -740,6 +741,13 @@ bool TabBar::select_next_available() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void TabBar::set_tab_offset(int p_offset) {
|
||||
ERR_FAIL_INDEX(p_offset, tabs.size());
|
||||
offset = p_offset;
|
||||
_update_cache();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
int TabBar::get_tab_offset() const {
|
||||
return offset;
|
||||
}
|
||||
|
|
@ -1237,7 +1245,7 @@ Variant TabBar::_handle_get_drag_data(const String &p_type, const Point2 &p_poin
|
|||
|
||||
HBoxContainer *drag_preview = memnew(HBoxContainer);
|
||||
|
||||
if (!tabs[tab_over].icon.is_null()) {
|
||||
if (tabs[tab_over].icon.is_valid()) {
|
||||
const Size2 icon_size = _get_tab_icon_size(tab_over);
|
||||
|
||||
TextureRect *tf = memnew(TextureRect);
|
||||
|
|
@ -1250,6 +1258,7 @@ Variant TabBar::_handle_get_drag_data(const String &p_type, const Point2 &p_poin
|
|||
}
|
||||
|
||||
Label *label = memnew(Label(get_tab_title(tab_over)));
|
||||
label->set_auto_translate_mode(get_auto_translate_mode()); // Reflect how the title is displayed.
|
||||
drag_preview->add_child(label);
|
||||
|
||||
set_drag_preview(drag_preview);
|
||||
|
|
@ -1479,8 +1488,8 @@ int TabBar::get_tab_width(int p_idx) const {
|
|||
style = theme_cache.tab_disabled_style;
|
||||
} else if (current == p_idx) {
|
||||
style = theme_cache.tab_selected_style;
|
||||
// Use the unselected style's width if the hovered one is shorter, to avoid an infinite loop when switching tabs with the mouse.
|
||||
} else if (hover == p_idx && theme_cache.tab_hovered_style->get_minimum_size().width >= theme_cache.tab_unselected_style->get_minimum_size().width) {
|
||||
// Always pick the widest style between hovered and unselected, to avoid an infinite loop when switching tabs with the mouse.
|
||||
} else if (theme_cache.tab_hovered_style->get_minimum_size().width > theme_cache.tab_unselected_style->get_minimum_size().width) {
|
||||
style = theme_cache.tab_hovered_style;
|
||||
} else {
|
||||
style = theme_cache.tab_unselected_style;
|
||||
|
|
|
|||
|
|
@ -117,8 +117,9 @@ private:
|
|||
bool scroll_to_selected = true;
|
||||
int tabs_rearrange_group = -1;
|
||||
|
||||
static const int CURRENT_TAB_UNINITIALIZED = -2;
|
||||
bool initialized = false;
|
||||
int queued_current = -1;
|
||||
int queued_current = CURRENT_TAB_UNINITIALIZED;
|
||||
|
||||
const float DEFAULT_GAMEPAD_EVENT_DELAY_MS = 0.5;
|
||||
const float GAMEPAD_EVENT_REPEAT_RATE_MS = 1.0 / 20;
|
||||
|
|
@ -249,6 +250,7 @@ public:
|
|||
bool select_previous_available();
|
||||
bool select_next_available();
|
||||
|
||||
void set_tab_offset(int p_offset);
|
||||
int get_tab_offset() const;
|
||||
bool get_offset_buttons_visible() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ void TabContainer::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (!is_visible() || setup_current_tab > -2) {
|
||||
if (!is_visible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +200,7 @@ void TabContainer::_notification(int p_what) {
|
|||
// beat it to the punch and make sure that the correct node is the only one visible first.
|
||||
// Otherwise, it can prevent a tab change done right before this container was made visible.
|
||||
Vector<Control *> controls = _get_tab_controls();
|
||||
int current = get_current_tab();
|
||||
int current = setup_current_tab > -2 ? setup_current_tab : get_current_tab();
|
||||
for (int i = 0; i < controls.size(); i++) {
|
||||
controls[i]->set_visible(i == current);
|
||||
}
|
||||
|
|
@ -267,10 +267,14 @@ void TabContainer::_repaint() {
|
|||
Vector<Control *> controls = _get_tab_controls();
|
||||
int current = get_current_tab();
|
||||
|
||||
// Move the TabBar to the top or bottom.
|
||||
// Don't change the left and right offsets since the TabBar will resize and may change tab offset.
|
||||
if (tabs_position == POSITION_BOTTOM) {
|
||||
tab_bar->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE);
|
||||
tab_bar->set_anchor_and_offset(SIDE_BOTTOM, 1.0, 0.0);
|
||||
tab_bar->set_anchor_and_offset(SIDE_TOP, 1.0, -_get_tab_height());
|
||||
} else {
|
||||
tab_bar->set_anchors_and_offsets_preset(PRESET_TOP_WIDE);
|
||||
tab_bar->set_anchor_and_offset(SIDE_BOTTOM, 0.0, _get_tab_height());
|
||||
tab_bar->set_anchor_and_offset(SIDE_TOP, 0.0, 0.0);
|
||||
}
|
||||
|
||||
updating_visibility = true;
|
||||
|
|
@ -299,7 +303,6 @@ void TabContainer::_repaint() {
|
|||
}
|
||||
updating_visibility = false;
|
||||
|
||||
_update_margins();
|
||||
update_minimum_size();
|
||||
}
|
||||
|
||||
|
|
@ -362,7 +365,7 @@ void TabContainer::_on_mouse_exited() {
|
|||
Vector<Control *> TabContainer::_get_tab_controls() const {
|
||||
Vector<Control *> controls;
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *control = as_sortable_control(get_child(i), SortableVisbilityMode::IGNORE);
|
||||
Control *control = as_sortable_control(get_child(i), SortableVisibilityMode::IGNORE);
|
||||
if (!control || control == tab_bar || children_removing.has(control)) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -539,7 +542,7 @@ void TabContainer::add_child_notify(Node *p_child) {
|
|||
return;
|
||||
}
|
||||
|
||||
Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE);
|
||||
Control *c = as_sortable_control(p_child, SortableVisibilityMode::IGNORE);
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -569,7 +572,7 @@ void TabContainer::move_child_notify(Node *p_child) {
|
|||
return;
|
||||
}
|
||||
|
||||
Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE);
|
||||
Control *c = as_sortable_control(p_child, SortableVisibilityMode::IGNORE);
|
||||
if (c) {
|
||||
tab_bar->move_tab(c->get_meta("_tab_index"), get_tab_idx_from_control(c));
|
||||
}
|
||||
|
|
@ -584,7 +587,7 @@ void TabContainer::remove_child_notify(Node *p_child) {
|
|||
return;
|
||||
}
|
||||
|
||||
Control *c = as_sortable_control(p_child, SortableVisbilityMode::IGNORE);
|
||||
Control *c = as_sortable_control(p_child, SortableVisibilityMode::IGNORE);
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -758,6 +761,7 @@ void TabContainer::set_all_tabs_in_front(bool p_in_front) {
|
|||
|
||||
remove_child(tab_bar);
|
||||
add_child(tab_bar, false, all_tabs_in_front ? INTERNAL_MODE_FRONT : INTERNAL_MODE_BACK);
|
||||
tab_bar->force_parent_owned();
|
||||
}
|
||||
|
||||
bool TabContainer::is_all_tabs_in_front() const {
|
||||
|
|
@ -909,7 +913,7 @@ Size2 TabContainer::get_minimum_size() const {
|
|||
for (int i = 0; i < controls.size(); i++) {
|
||||
Control *c = controls[i];
|
||||
|
||||
if (!c->is_visible_in_tree() && !use_hidden_tabs_for_min_size) {
|
||||
if (!c->is_visible() && !use_hidden_tabs_for_min_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,8 +34,13 @@ void TextEdit::_set_selection_mode_compat_86978(SelectionMode p_mode, int p_line
|
|||
set_selection_mode(p_mode);
|
||||
}
|
||||
|
||||
Point2i TextEdit::_get_line_column_at_pos_bind_compat_100913(const Point2i &p_pos, bool p_allow_out_of_bounds) const {
|
||||
return get_line_column_at_pos(p_pos, p_allow_out_of_bounds, true);
|
||||
}
|
||||
|
||||
void TextEdit::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("set_selection_mode", "mode", "line", "column", "caret_index"), &TextEdit::_set_selection_mode_compat_86978, DEFVAL(-1), DEFVAL(-1), DEFVAL(0));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_line_column_at_pos", "position", "allow_out_of_bounds"), &TextEdit::_get_line_column_at_pos_bind_compat_100913, DEFVAL(true));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -110,6 +110,7 @@ public:
|
|||
MENU_INSERT_ZWNJ,
|
||||
MENU_INSERT_WJ,
|
||||
MENU_INSERT_SHY,
|
||||
MENU_EMOJI_AND_SYMBOL,
|
||||
MENU_MAX
|
||||
|
||||
};
|
||||
|
|
@ -139,7 +140,7 @@ private:
|
|||
Variant metadata;
|
||||
bool clickable = false;
|
||||
|
||||
Ref<Texture2D> icon = Ref<Texture2D>();
|
||||
Ref<Texture2D> icon;
|
||||
String text = "";
|
||||
Color color = Color(1, 1, 1);
|
||||
};
|
||||
|
|
@ -151,8 +152,12 @@ private:
|
|||
Array bidi_override;
|
||||
Ref<TextParagraph> data_buf;
|
||||
|
||||
String ime_data;
|
||||
Array ime_bidi_override;
|
||||
|
||||
Color background_color = Color(0, 0, 0, 0);
|
||||
bool hidden = false;
|
||||
int line_count = 0;
|
||||
int height = 0;
|
||||
int width = 0;
|
||||
|
||||
|
|
@ -178,17 +183,17 @@ private:
|
|||
bool use_default_word_separators = true;
|
||||
bool use_custom_word_separators = false;
|
||||
|
||||
int line_height = -1;
|
||||
int max_width = -1;
|
||||
mutable bool max_line_width_dirty = true;
|
||||
mutable bool max_line_height_dirty = true;
|
||||
mutable int max_line_width = 0;
|
||||
mutable int max_line_height = 0;
|
||||
mutable int total_visible_line_count = 0;
|
||||
int width = -1;
|
||||
|
||||
int tab_size = 4;
|
||||
int gutter_count = 0;
|
||||
bool indent_wrapped_lines = false;
|
||||
|
||||
void _calculate_line_height();
|
||||
void _calculate_max_line_width();
|
||||
|
||||
public:
|
||||
void set_tab_size(int p_tab_size);
|
||||
int get_tab_size() const;
|
||||
|
|
@ -203,6 +208,7 @@ private:
|
|||
int get_line_height() const;
|
||||
int get_line_width(int p_line, int p_wrap_index = -1) const;
|
||||
int get_max_width() const;
|
||||
int get_total_visible_line_count() const;
|
||||
|
||||
void set_use_default_word_separators(bool p_enabled);
|
||||
bool is_default_word_separators_enabled() const;
|
||||
|
|
@ -210,7 +216,6 @@ private:
|
|||
void set_use_custom_word_separators(bool p_enabled);
|
||||
bool is_custom_word_separators_enabled() const;
|
||||
|
||||
void set_word_separators(const String &p_separators);
|
||||
void set_custom_word_separators(const String &p_separators);
|
||||
String get_enabled_word_separators() const;
|
||||
String get_custom_word_separators() const;
|
||||
|
|
@ -226,29 +231,21 @@ private:
|
|||
const Ref<TextParagraph> get_line_data(int p_line) const;
|
||||
|
||||
void set(int p_line, const String &p_text, const Array &p_bidi_override);
|
||||
void set_hidden(int p_line, bool p_hidden) {
|
||||
if (text[p_line].hidden == p_hidden) {
|
||||
return;
|
||||
}
|
||||
text.write[p_line].hidden = p_hidden;
|
||||
if (!p_hidden && text[p_line].width > max_width) {
|
||||
max_width = text[p_line].width;
|
||||
} else if (p_hidden && text[p_line].width == max_width) {
|
||||
_calculate_max_line_width();
|
||||
}
|
||||
}
|
||||
bool is_hidden(int p_line) const { return text[p_line].hidden; }
|
||||
void set_ime(int p_line, const String &p_text, const Array &p_bidi_override);
|
||||
void set_hidden(int p_line, bool p_hidden);
|
||||
bool is_hidden(int p_line) const;
|
||||
void insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override);
|
||||
void remove_range(int p_from_line, int p_to_line);
|
||||
int size() const { return text.size(); }
|
||||
void clear();
|
||||
|
||||
void invalidate_cache(int p_line, int p_column = -1, bool p_text_changed = false, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
|
||||
void invalidate_cache(int p_line, bool p_text_changed = false);
|
||||
void invalidate_font();
|
||||
void invalidate_all();
|
||||
void invalidate_all_lines();
|
||||
|
||||
_FORCE_INLINE_ const String &operator[](int p_line) const;
|
||||
_FORCE_INLINE_ String operator[](int p_line) const;
|
||||
_FORCE_INLINE_ const String &get_text_with_ime(int p_line) const;
|
||||
|
||||
/* Gutters. */
|
||||
void add_gutter(int p_at);
|
||||
|
|
@ -281,6 +278,7 @@ private:
|
|||
bool setting_text = false;
|
||||
|
||||
bool alt_start = false;
|
||||
bool alt_start_no_hold = false;
|
||||
uint32_t alt_code = 0;
|
||||
|
||||
// Text properties.
|
||||
|
|
@ -294,7 +292,7 @@ private:
|
|||
int placeholder_line_height = -1;
|
||||
int placeholder_max_width = -1;
|
||||
|
||||
Vector<String> placeholder_wraped_rows;
|
||||
Vector<String> placeholder_wrapped_rows;
|
||||
|
||||
void _update_placeholder();
|
||||
bool _using_placeholder() const;
|
||||
|
|
@ -320,9 +318,11 @@ private:
|
|||
// User control.
|
||||
bool overtype_mode = false;
|
||||
bool context_menu_enabled = true;
|
||||
bool emoji_menu_enabled = true;
|
||||
bool shortcut_keys_enabled = true;
|
||||
bool virtual_keyboard_enabled = true;
|
||||
bool middle_mouse_paste_enabled = true;
|
||||
bool empty_selection_clipboard_enabled = true;
|
||||
|
||||
// Overridable actions.
|
||||
String cut_copy_line = "";
|
||||
|
|
@ -453,6 +453,7 @@ private:
|
|||
void _caret_changed(int p_caret = -1);
|
||||
void _emit_caret_changed();
|
||||
|
||||
void _show_virtual_keyboard();
|
||||
void _reset_caret_blink_timer();
|
||||
void _toggle_draw_caret();
|
||||
|
||||
|
|
@ -505,8 +506,9 @@ private:
|
|||
HScrollBar *h_scroll = nullptr;
|
||||
VScrollBar *v_scroll = nullptr;
|
||||
|
||||
float content_height_cache = 0.0;
|
||||
Vector2i content_size_cache;
|
||||
bool fit_content_height = false;
|
||||
bool fit_content_width = false;
|
||||
bool scroll_past_end_of_file_enabled = false;
|
||||
|
||||
// Smooth scrolling.
|
||||
|
|
@ -537,6 +539,8 @@ private:
|
|||
void _scroll_lines_up();
|
||||
void _scroll_lines_down();
|
||||
|
||||
void _adjust_viewport_to_caret_horizontally(int p_caret = 0);
|
||||
|
||||
// Minimap.
|
||||
bool draw_minimap = false;
|
||||
|
||||
|
|
@ -564,11 +568,14 @@ private:
|
|||
Vector2i hovered_gutter = Vector2i(-1, -1); // X = gutter index, Y = row.
|
||||
|
||||
void _update_gutter_width();
|
||||
Vector2i _get_hovered_gutter(const Point2 &p_mouse_pos) const;
|
||||
|
||||
/* Syntax highlighting. */
|
||||
Ref<SyntaxHighlighter> syntax_highlighter;
|
||||
HashMap<int, Vector<Pair<int64_t, Color>>> syntax_highlighting_cache;
|
||||
|
||||
Dictionary _get_line_syntax_highlighting(int p_line);
|
||||
Vector<Pair<int64_t, Color>> _get_line_syntax_highlighting(int p_line);
|
||||
void _clear_syntax_highlighting_cache();
|
||||
|
||||
/* Visual. */
|
||||
struct ThemeCache {
|
||||
|
|
@ -655,6 +662,7 @@ protected:
|
|||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _set_selection_mode_compat_86978(SelectionMode p_mode, int p_line = -1, int p_column = -1, int p_caret = 0);
|
||||
Point2i _get_line_column_at_pos_bind_compat_100913(const Point2i &p_pos, bool p_allow_out_of_bounds = true) const;
|
||||
static void _bind_compatibility_methods();
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
|
|
@ -692,9 +700,9 @@ protected:
|
|||
void _set_symbol_lookup_word(const String &p_symbol);
|
||||
|
||||
// Theme items.
|
||||
virtual Color _get_brace_mismatch_color() const { return Color(); };
|
||||
virtual Color _get_code_folding_color() const { return Color(); };
|
||||
virtual Ref<Texture2D> _get_folded_eol_icon() const { return Ref<Texture2D>(); };
|
||||
virtual Color _get_brace_mismatch_color() const { return Color(); }
|
||||
virtual Color _get_code_folding_color() const { return Color(); }
|
||||
virtual Ref<Texture2D> _get_folded_eol_icon() const { return Ref<Texture2D>(); }
|
||||
|
||||
/* Text manipulation */
|
||||
|
||||
|
|
@ -734,7 +742,7 @@ public:
|
|||
void cancel_ime();
|
||||
void apply_ime();
|
||||
|
||||
void set_editable(const bool p_editable);
|
||||
void set_editable(bool p_editable);
|
||||
bool is_editable() const;
|
||||
|
||||
void set_text_direction(TextDirection p_text_direction);
|
||||
|
|
@ -755,12 +763,17 @@ public:
|
|||
bool is_indent_wrapped_lines() const;
|
||||
|
||||
// User controls
|
||||
void set_overtype_mode_enabled(const bool p_enabled);
|
||||
void set_overtype_mode_enabled(bool p_enabled);
|
||||
bool is_overtype_mode_enabled() const;
|
||||
|
||||
void set_context_menu_enabled(bool p_enabled);
|
||||
bool is_context_menu_enabled() const;
|
||||
|
||||
void show_emoji_and_symbol_picker();
|
||||
|
||||
void set_emoji_menu_enabled(bool p_enabled);
|
||||
bool is_emoji_menu_enabled() const;
|
||||
|
||||
void set_shortcut_keys_enabled(bool p_enabled);
|
||||
bool is_shortcut_keys_enabled() const;
|
||||
|
||||
|
|
@ -770,6 +783,9 @@ public:
|
|||
void set_middle_mouse_paste_enabled(bool p_enabled);
|
||||
bool is_middle_mouse_paste_enabled() const;
|
||||
|
||||
void set_empty_selection_clipboard_enabled(bool p_enabled);
|
||||
bool is_empty_selection_clipboard_enabled() const;
|
||||
|
||||
// Text manipulation
|
||||
void clear();
|
||||
|
||||
|
|
@ -783,6 +799,7 @@ public:
|
|||
|
||||
void set_line(int p_line, const String &p_new_text);
|
||||
String get_line(int p_line) const;
|
||||
String get_line_with_ime(int p_line) const;
|
||||
|
||||
int get_line_width(int p_line, int p_wrap_index = -1) const;
|
||||
int get_line_height() const;
|
||||
|
|
@ -849,7 +866,7 @@ public:
|
|||
|
||||
String get_word_at_pos(const Vector2 &p_pos) const;
|
||||
|
||||
Point2i get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_of_bounds = true) const;
|
||||
Point2i get_line_column_at_pos(const Point2i &p_pos, bool p_clamp_line = true, bool p_clamp_column = true) const;
|
||||
Point2i get_pos_at_line_column(int p_line, int p_column) const;
|
||||
Rect2i get_rect_at_line_column(int p_line, int p_column) const;
|
||||
|
||||
|
|
@ -862,7 +879,7 @@ public:
|
|||
void set_caret_type(CaretType p_type);
|
||||
CaretType get_caret_type() const;
|
||||
|
||||
void set_caret_blink_enabled(const bool p_enabled);
|
||||
void set_caret_blink_enabled(bool p_enabled);
|
||||
bool is_caret_blink_enabled() const;
|
||||
|
||||
void set_caret_blink_interval(const float p_interval);
|
||||
|
|
@ -871,10 +888,10 @@ public:
|
|||
void set_draw_caret_when_editable_disabled(bool p_enable);
|
||||
bool is_drawing_caret_when_editable_disabled() const;
|
||||
|
||||
void set_move_caret_on_right_click_enabled(const bool p_enabled);
|
||||
void set_move_caret_on_right_click_enabled(bool p_enabled);
|
||||
bool is_move_caret_on_right_click_enabled() const;
|
||||
|
||||
void set_caret_mid_grapheme_enabled(const bool p_enabled);
|
||||
void set_caret_mid_grapheme_enabled(bool p_enabled);
|
||||
bool is_caret_mid_grapheme_enabled() const;
|
||||
|
||||
void set_multiple_carets_enabled(bool p_enabled);
|
||||
|
|
@ -910,13 +927,13 @@ public:
|
|||
String get_word_under_caret(int p_caret = -1) const;
|
||||
|
||||
/* Selection. */
|
||||
void set_selecting_enabled(const bool p_enabled);
|
||||
void set_selecting_enabled(bool p_enabled);
|
||||
bool is_selecting_enabled() const;
|
||||
|
||||
void set_deselect_on_focus_loss_enabled(const bool p_enabled);
|
||||
void set_deselect_on_focus_loss_enabled(bool p_enabled);
|
||||
bool is_deselect_on_focus_loss_enabled() const;
|
||||
|
||||
void set_drag_and_drop_selection_enabled(const bool p_enabled);
|
||||
void set_drag_and_drop_selection_enabled(bool p_enabled);
|
||||
bool is_drag_and_drop_selection_enabled() const;
|
||||
|
||||
void set_selection_mode(SelectionMode p_mode);
|
||||
|
|
@ -965,10 +982,10 @@ public:
|
|||
|
||||
/* Viewport. */
|
||||
// Scrolling.
|
||||
void set_smooth_scroll_enabled(const bool p_enabled);
|
||||
void set_smooth_scroll_enabled(bool p_enabled);
|
||||
bool is_smooth_scroll_enabled() const;
|
||||
|
||||
void set_scroll_past_end_of_file_enabled(const bool p_enabled);
|
||||
void set_scroll_past_end_of_file_enabled(bool p_enabled);
|
||||
bool is_scroll_past_end_of_file_enabled() const;
|
||||
|
||||
VScrollBar *get_v_scroll_bar() const;
|
||||
|
|
@ -983,9 +1000,12 @@ 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);
|
||||
void set_fit_content_height_enabled(bool p_enabled);
|
||||
bool is_fit_content_height_enabled() const;
|
||||
|
||||
void set_fit_content_width_enabled(bool p_enabled);
|
||||
bool is_fit_content_width_enabled() const;
|
||||
|
||||
double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const;
|
||||
|
||||
// Visible lines.
|
||||
|
|
@ -1019,6 +1039,7 @@ public:
|
|||
void add_gutter(int p_at = -1);
|
||||
void remove_gutter(int p_gutter);
|
||||
int get_gutter_count() const;
|
||||
Vector2i get_hovered_gutter() const { return hovered_gutter; }
|
||||
|
||||
void set_gutter_name(int p_gutter, const String &p_name);
|
||||
String get_gutter_name(int p_gutter) const;
|
||||
|
|
@ -1071,7 +1092,7 @@ public:
|
|||
void set_highlight_current_line(bool p_enabled);
|
||||
bool is_highlight_current_line_enabled() const;
|
||||
|
||||
void set_highlight_all_occurrences(const bool p_enabled);
|
||||
void set_highlight_all_occurrences(bool p_enabled);
|
||||
bool is_highlight_all_occurrences_enabled() const;
|
||||
|
||||
void set_draw_control_chars(bool p_enabled);
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@
|
|||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
Size2 TextureButton::get_minimum_size() const {
|
||||
Size2 rscale = Control::get_minimum_size();
|
||||
|
||||
|
|
@ -173,7 +171,7 @@ void TextureButton::_notification(int p_what) {
|
|||
bool draw_focus = (has_focus() && focused.is_valid());
|
||||
|
||||
// If no other texture is valid, try using focused texture.
|
||||
bool draw_focus_only = draw_focus && !texdraw.is_valid();
|
||||
bool draw_focus_only = draw_focus && texdraw.is_null();
|
||||
if (draw_focus_only) {
|
||||
texdraw = focused;
|
||||
}
|
||||
|
|
@ -340,11 +338,11 @@ Ref<BitMap> TextureButton::get_click_mask() const {
|
|||
|
||||
Ref<Texture2D> TextureButton::get_texture_focused() const {
|
||||
return focused;
|
||||
};
|
||||
}
|
||||
|
||||
void TextureButton::set_texture_focused(const Ref<Texture2D> &p_focused) {
|
||||
focused = p_focused;
|
||||
};
|
||||
_set_texture(&focused, p_focused);
|
||||
}
|
||||
|
||||
void TextureButton::_set_texture(Ref<Texture2D> *p_destination, const Ref<Texture2D> &p_texture) {
|
||||
DEV_ASSERT(p_destination);
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include "texture_progress_bar.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
|
||||
void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) {
|
||||
_set_texture(&under, p_texture);
|
||||
}
|
||||
|
|
@ -83,15 +81,19 @@ bool TextureProgressBar::get_nine_patch_stretch() const {
|
|||
Size2 TextureProgressBar::get_minimum_size() const {
|
||||
if (nine_patch_stretch) {
|
||||
return Size2(stretch_margin[SIDE_LEFT] + stretch_margin[SIDE_RIGHT], stretch_margin[SIDE_TOP] + stretch_margin[SIDE_BOTTOM]);
|
||||
} else if (under.is_valid()) {
|
||||
return under->get_size();
|
||||
} else if (over.is_valid()) {
|
||||
return over->get_size();
|
||||
} else if (progress.is_valid()) {
|
||||
return progress->get_size();
|
||||
}
|
||||
|
||||
return Size2(1, 1);
|
||||
Size2 size = Size2(1, 1);
|
||||
if (under.is_valid()) {
|
||||
size = size.max(under->get_size());
|
||||
}
|
||||
if (progress.is_valid()) {
|
||||
size = size.max(progress->get_size());
|
||||
}
|
||||
if (over.is_valid()) {
|
||||
size = size.max(over->get_size());
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void TextureProgressBar::set_progress_texture(const Ref<Texture2D> &p_texture) {
|
||||
|
|
@ -428,36 +430,20 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu
|
|||
void TextureProgressBar::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_DRAW: {
|
||||
if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP || mode == FILL_BILINEAR_LEFT_AND_RIGHT || mode == FILL_BILINEAR_TOP_AND_BOTTOM)) {
|
||||
if (under.is_valid()) {
|
||||
if (under.is_valid()) {
|
||||
if (nine_patch_stretch) {
|
||||
draw_nine_patch_stretched(under, mode, 1.0, tint_under);
|
||||
} else {
|
||||
draw_texture(under, Point2(), tint_under);
|
||||
}
|
||||
if (progress.is_valid()) {
|
||||
}
|
||||
|
||||
if (progress.is_valid()) {
|
||||
const bool is_radial_mode = (mode == FILL_CLOCKWISE || mode == FILL_COUNTER_CLOCKWISE || mode == FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE);
|
||||
|
||||
if (nine_patch_stretch && !is_radial_mode) {
|
||||
draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress);
|
||||
}
|
||||
if (over.is_valid()) {
|
||||
draw_nine_patch_stretched(over, mode, 1.0, tint_over);
|
||||
}
|
||||
} else {
|
||||
if (under.is_valid()) {
|
||||
switch (mode) {
|
||||
case FILL_CLOCKWISE:
|
||||
case FILL_COUNTER_CLOCKWISE:
|
||||
case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: {
|
||||
if (nine_patch_stretch) {
|
||||
Rect2 region = Rect2(Point2(), get_size());
|
||||
draw_texture_rect(under, region, false, tint_under);
|
||||
} else {
|
||||
draw_texture(under, Point2(), tint_under);
|
||||
}
|
||||
} break;
|
||||
case FILL_MODE_MAX:
|
||||
break;
|
||||
default:
|
||||
draw_texture(under, Point2(), tint_under);
|
||||
}
|
||||
}
|
||||
if (progress.is_valid()) {
|
||||
} else {
|
||||
Size2 s = progress->get_size();
|
||||
switch (mode) {
|
||||
case FILL_LEFT_TO_RIGHT: {
|
||||
|
|
@ -534,23 +520,6 @@ void TextureProgressBar::_notification(int p_what) {
|
|||
draw_polygon(points, colors, uvs, progress);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw a reference cross.
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
Point2 p;
|
||||
|
||||
if (nine_patch_stretch) {
|
||||
p = get_size();
|
||||
} else {
|
||||
p = progress->get_size();
|
||||
}
|
||||
|
||||
p *= get_relative_center();
|
||||
p += progress_offset;
|
||||
p = p.floor();
|
||||
draw_line(p - Point2(8, 0), p + Point2(8, 0), Color(0.9, 0.5, 0.5), 2);
|
||||
draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2);
|
||||
}
|
||||
} break;
|
||||
case FILL_BILINEAR_LEFT_AND_RIGHT: {
|
||||
Rect2 region = Rect2(progress_offset + Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y));
|
||||
|
|
@ -564,27 +533,32 @@ void TextureProgressBar::_notification(int p_what) {
|
|||
} break;
|
||||
case FILL_MODE_MAX:
|
||||
break;
|
||||
default:
|
||||
draw_texture_rect_region(progress, Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress);
|
||||
}
|
||||
}
|
||||
if (over.is_valid()) {
|
||||
switch (mode) {
|
||||
case FILL_CLOCKWISE:
|
||||
case FILL_COUNTER_CLOCKWISE:
|
||||
case FILL_CLOCKWISE_AND_COUNTER_CLOCKWISE: {
|
||||
if (nine_patch_stretch) {
|
||||
Rect2 region = Rect2(Point2(), get_size());
|
||||
draw_texture_rect(over, region, false, tint_over);
|
||||
} else {
|
||||
draw_texture(over, Point2(), tint_over);
|
||||
}
|
||||
} break;
|
||||
case FILL_MODE_MAX:
|
||||
break;
|
||||
default:
|
||||
draw_texture(over, Point2(), tint_over);
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Draw a reference cross for radial modes.
|
||||
if (is_radial_mode && is_part_of_edited_scene()) {
|
||||
Point2 p;
|
||||
|
||||
if (nine_patch_stretch) {
|
||||
p = get_size();
|
||||
} else {
|
||||
p = progress->get_size();
|
||||
}
|
||||
|
||||
p *= get_relative_center();
|
||||
p += progress_offset;
|
||||
draw_line(p - Point2(8, 0), p + Point2(8, 0), Color(0.9, 0.5, 0.5), 2);
|
||||
draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (over.is_valid()) {
|
||||
if (nine_patch_stretch) {
|
||||
draw_nine_patch_stretched(over, mode, 1.0, tint_over);
|
||||
} else {
|
||||
draw_texture(over, Point2(), tint_over);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
|
@ -608,11 +582,10 @@ int TextureProgressBar::get_fill_mode() {
|
|||
}
|
||||
|
||||
void TextureProgressBar::set_radial_initial_angle(float p_angle) {
|
||||
while (p_angle > 360) {
|
||||
p_angle -= 360;
|
||||
}
|
||||
while (p_angle < 0) {
|
||||
p_angle += 360;
|
||||
ERR_FAIL_COND_MSG(!Math::is_finite(p_angle), "Angle is non-finite.");
|
||||
|
||||
if (p_angle < 0.0 || p_angle > 360.0) {
|
||||
p_angle = Math::fposmodp(p_angle, 360.0f);
|
||||
}
|
||||
|
||||
if (rad_init_angle == p_angle) {
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
|
||||
#include "texture_rect.h"
|
||||
|
||||
#include "servers/rendering_server.h"
|
||||
|
||||
void TextureRect::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_DRAW: {
|
||||
|
|
@ -107,7 +105,7 @@ void TextureRect::_notification(int p_what) {
|
|||
}
|
||||
|
||||
Size2 TextureRect::get_minimum_size() const {
|
||||
if (!texture.is_null()) {
|
||||
if (texture.is_valid()) {
|
||||
switch (expand_mode) {
|
||||
case EXPAND_KEEP_SIZE: {
|
||||
return texture->get_size();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -60,9 +60,11 @@ private:
|
|||
TreeCellMode mode = TreeItem::CELL_MODE_STRING;
|
||||
|
||||
Ref<Texture2D> icon;
|
||||
Ref<Texture2D> icon_overlay;
|
||||
Rect2i icon_region;
|
||||
String text;
|
||||
String xl_text;
|
||||
Node::AutoTranslateMode auto_translate_mode = Node::AUTO_TRANSLATE_MODE_INHERIT;
|
||||
bool edit_multiline = false;
|
||||
String suffix;
|
||||
Ref<TextParagraph> text_buf;
|
||||
|
|
@ -136,6 +138,7 @@ private:
|
|||
TreeItem *prev = nullptr; // previous in list
|
||||
TreeItem *next = nullptr; // next in list
|
||||
TreeItem *first_child = nullptr;
|
||||
TreeItem *last_child = nullptr;
|
||||
|
||||
Vector<TreeItem *> children_cache;
|
||||
bool is_root = false; // for tree root
|
||||
|
|
@ -177,6 +180,9 @@ private:
|
|||
if (parent->first_child == this) {
|
||||
parent->first_child = next;
|
||||
}
|
||||
if (parent->last_child == this) {
|
||||
parent->last_child = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,6 +211,10 @@ public:
|
|||
void set_cell_mode(int p_column, TreeCellMode p_mode);
|
||||
TreeCellMode get_cell_mode(int p_column) const;
|
||||
|
||||
/* auto translate mode */
|
||||
void set_auto_translate_mode(int p_column, Node::AutoTranslateMode p_mode);
|
||||
Node::AutoTranslateMode get_auto_translate_mode(int p_column) const;
|
||||
|
||||
/* multiline editable */
|
||||
void set_edit_multiline(int p_column, bool p_multiline);
|
||||
bool is_edit_multiline(int p_column) const;
|
||||
|
|
@ -217,6 +227,8 @@ public:
|
|||
|
||||
void propagate_check(int p_column, bool p_emit_signal = true);
|
||||
|
||||
String atr(int p_column, const String &p_text) const;
|
||||
|
||||
private:
|
||||
// Check helpers.
|
||||
void _propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal);
|
||||
|
|
@ -253,6 +265,9 @@ public:
|
|||
void set_icon(int p_column, const Ref<Texture2D> &p_icon);
|
||||
Ref<Texture2D> get_icon(int p_column) const;
|
||||
|
||||
void set_icon_overlay(int p_column, const Ref<Texture2D> &p_icon_overlay);
|
||||
Ref<Texture2D> get_icon_overlay(int p_column) const;
|
||||
|
||||
void set_icon_region(int p_column, const Rect2 &p_icon_region);
|
||||
Rect2 get_icon_region(int p_column) const;
|
||||
|
||||
|
|
@ -262,6 +277,7 @@ public:
|
|||
void set_icon_max_width(int p_column, int p_max);
|
||||
int get_icon_max_width(int p_column) const;
|
||||
|
||||
void clear_buttons();
|
||||
void add_button(int p_column, const Ref<Texture2D> &p_button, int p_id = -1, bool p_disabled = false, const String &p_tooltip = "");
|
||||
int get_button_count(int p_column) const;
|
||||
String get_button_tooltip_text(int p_column, int p_index) const;
|
||||
|
|
@ -435,6 +451,9 @@ private:
|
|||
Vector2 pressing_pos;
|
||||
Rect2 pressing_item_rect;
|
||||
|
||||
Vector2 hovered_pos;
|
||||
bool is_mouse_hovering = false;
|
||||
|
||||
float range_drag_base = 0.0;
|
||||
bool range_drag_enabled = false;
|
||||
Vector2 range_drag_capture_pos;
|
||||
|
|
@ -479,7 +498,7 @@ private:
|
|||
|
||||
VBoxContainer *popup_editor_vb = nullptr;
|
||||
|
||||
bool popup_edit_commited = true;
|
||||
bool popup_edit_committed = true;
|
||||
Popup *popup_editor = nullptr;
|
||||
LineEdit *line_editor = nullptr;
|
||||
TextEdit *text_editor = nullptr;
|
||||
|
|
@ -499,8 +518,8 @@ private:
|
|||
int get_item_height(TreeItem *p_item) const;
|
||||
void _update_all();
|
||||
void update_column(int p_col);
|
||||
void update_item_cell(TreeItem *p_item, int p_col);
|
||||
void update_item_cache(TreeItem *p_item);
|
||||
void update_item_cell(TreeItem *p_item, int p_col) const;
|
||||
void update_item_cache(TreeItem *p_item) const;
|
||||
//void draw_item_text(String p_text,const Ref<Texture2D>& p_icon,int p_icon_max_w,bool p_tool,Rect2i p_rect,const Color& p_color);
|
||||
void draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Color &p_color, const Color &p_icon_color, int p_ol_size, const Color &p_ol_color);
|
||||
int draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 &p_draw_size, TreeItem *p_item, int &r_self_height);
|
||||
|
|
@ -530,10 +549,13 @@ private:
|
|||
int font_size = 0;
|
||||
int tb_font_size = 0;
|
||||
|
||||
Ref<StyleBox> hovered;
|
||||
Ref<StyleBox> hovered_dimmed;
|
||||
Ref<StyleBox> selected;
|
||||
Ref<StyleBox> selected_focus;
|
||||
Ref<StyleBox> cursor;
|
||||
Ref<StyleBox> cursor_unfocus;
|
||||
Ref<StyleBox> button_hover;
|
||||
Ref<StyleBox> button_pressed;
|
||||
Ref<StyleBox> title_button;
|
||||
Ref<StyleBox> title_button_hover;
|
||||
|
|
@ -557,6 +579,8 @@ private:
|
|||
Ref<Texture2D> updown;
|
||||
|
||||
Color font_color;
|
||||
Color font_hovered_color;
|
||||
Color font_hovered_dimmed_color;
|
||||
Color font_selected_color;
|
||||
Color font_disabled_color;
|
||||
Color guide_color;
|
||||
|
|
@ -608,16 +632,17 @@ private:
|
|||
};
|
||||
|
||||
ClickType click_type = Cache::CLICK_NONE;
|
||||
ClickType hover_type = Cache::CLICK_NONE;
|
||||
int click_index = -1;
|
||||
int click_id = -1;
|
||||
TreeItem *click_item = nullptr;
|
||||
int click_column = 0;
|
||||
int hover_index = -1;
|
||||
int hover_header_column = -1;
|
||||
bool hover_header_row = false;
|
||||
Point2 click_pos;
|
||||
|
||||
TreeItem *hover_item = nullptr;
|
||||
int hover_cell = -1;
|
||||
int hover_column = -1;
|
||||
int hover_button_index_in_column = -1;
|
||||
|
||||
bool rtl = false;
|
||||
} cache;
|
||||
|
|
@ -644,9 +669,19 @@ private:
|
|||
|
||||
TreeItem *_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards = false);
|
||||
|
||||
TreeItem *_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_column, int &h, int §ion) const;
|
||||
TreeItem *_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_column, int &r_height, int &r_section) const;
|
||||
|
||||
void _find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_column, int &r_index) const;
|
||||
void _find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_column, int &r_index, int &r_section) const;
|
||||
|
||||
struct FindColumnButtonResult {
|
||||
int column_index = -1;
|
||||
int button_index = -1;
|
||||
int column_width = -1;
|
||||
int column_offset = -1;
|
||||
int pos_x = -1;
|
||||
};
|
||||
|
||||
FindColumnButtonResult _find_column_and_button_at_pos(int p_x, const TreeItem *p_item, int p_x_ofs, int p_x_limit) const;
|
||||
|
||||
/* float drag_speed;
|
||||
float drag_accum;
|
||||
|
|
@ -674,6 +709,10 @@ private:
|
|||
|
||||
bool enable_recursive_folding = true;
|
||||
|
||||
bool enable_auto_tooltip = true;
|
||||
|
||||
void _determine_hovered_item();
|
||||
|
||||
int _count_selected_items(TreeItem *p_from) const;
|
||||
bool _is_branch_selected(TreeItem *p_from) const;
|
||||
bool _is_sibling_branch_selected(TreeItem *p_from) const;
|
||||
|
|
@ -686,6 +725,7 @@ private:
|
|||
|
||||
Rect2 _get_scrollbar_layout_rect() const;
|
||||
Rect2 _get_content_rect() const; // Considering the background stylebox and scrollbars.
|
||||
Rect2 _get_item_focus_rect(const TreeItem *p_item) const;
|
||||
|
||||
protected:
|
||||
virtual void _update_theme_item_cache() override;
|
||||
|
|
@ -803,6 +843,9 @@ public:
|
|||
void set_allow_search(bool p_allow);
|
||||
bool get_allow_search() const;
|
||||
|
||||
void set_auto_tooltip(bool p_enable);
|
||||
bool is_auto_tooltip_enabled() const;
|
||||
|
||||
Size2 get_minimum_size() const override;
|
||||
|
||||
Tree();
|
||||
|
|
|
|||
|
|
@ -81,10 +81,10 @@ void VideoStreamPlayer::_mix_audios(void *p_self) {
|
|||
|
||||
// Called from audio thread
|
||||
void VideoStreamPlayer::_mix_audio() {
|
||||
if (!stream.is_valid()) {
|
||||
if (stream.is_null()) {
|
||||
return;
|
||||
}
|
||||
if (!playback.is_valid() || !playback->is_playing() || playback->is_paused()) {
|
||||
if (playback.is_null() || !playback->is_playing() || playback->is_paused()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -158,6 +158,7 @@ void VideoStreamPlayer::_notification(int p_notification) {
|
|||
playback->update(delta); // playback->is_playing() returns false in the last video frame
|
||||
|
||||
if (!playback->is_playing()) {
|
||||
resampler.flush();
|
||||
if (loop) {
|
||||
play();
|
||||
return;
|
||||
|
|
@ -178,6 +179,7 @@ void VideoStreamPlayer::_notification(int p_notification) {
|
|||
draw_texture_rect(texture, Rect2(Point2(), s), false);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_SUSPENDED:
|
||||
case NOTIFICATION_PAUSED: {
|
||||
if (is_playing() && !is_paused()) {
|
||||
paused_from_tree = true;
|
||||
|
|
@ -189,6 +191,13 @@ void VideoStreamPlayer::_notification(int p_notification) {
|
|||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_UNSUSPENDED: {
|
||||
if (get_tree()->is_paused()) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
case NOTIFICATION_UNPAUSED: {
|
||||
if (paused_from_tree) {
|
||||
paused_from_tree = false;
|
||||
|
|
@ -203,7 +212,7 @@ void VideoStreamPlayer::_notification(int p_notification) {
|
|||
}
|
||||
|
||||
Size2 VideoStreamPlayer::get_minimum_size() const {
|
||||
if (!expand && !texture.is_null()) {
|
||||
if (!expand && texture.is_valid()) {
|
||||
return texture->get_size();
|
||||
} else {
|
||||
return Size2();
|
||||
|
|
@ -256,7 +265,7 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
|
|||
stream->connect_changed(callable_mp(this, &VideoStreamPlayer::set_stream).bind(stream));
|
||||
}
|
||||
|
||||
if (!playback.is_null()) {
|
||||
if (playback.is_valid()) {
|
||||
playback->set_paused(paused);
|
||||
texture = playback->get_texture();
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@
|
|||
#include "scene/gui/control.h"
|
||||
#include "scene/resources/video_stream.h"
|
||||
#include "servers/audio/audio_rb_resampler.h"
|
||||
#include "servers/audio_server.h"
|
||||
|
||||
class VideoStreamPlayer : public Control {
|
||||
GDCLASS(VideoStreamPlayer, Control);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "core/input/input.h"
|
||||
#include "core/input/shortcut.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) {
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
|
|
@ -109,8 +110,8 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
|
|||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (mm.is_valid()) {
|
||||
if (is_dragging) {
|
||||
if (p_canvas_rect != Rect2()) {
|
||||
pan_callback.call(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect), p_event);
|
||||
if (warped_panning_viewport && p_canvas_rect.has_area()) {
|
||||
pan_callback.call(warped_panning_viewport->wrap_mouse_in_rect(mm->get_relative(), p_canvas_rect), p_event);
|
||||
} else {
|
||||
pan_callback.call(mm->get_relative(), p_event);
|
||||
}
|
||||
|
|
@ -212,6 +213,10 @@ void ViewPanner::setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_
|
|||
set_simple_panning_enabled(p_simple_panning);
|
||||
}
|
||||
|
||||
void ViewPanner::setup_warped_panning(Viewport *p_viewport, bool p_allowed) {
|
||||
warped_panning_viewport = p_allowed ? p_viewport : nullptr;
|
||||
}
|
||||
|
||||
bool ViewPanner::is_panning() const {
|
||||
return is_dragging || pan_key_pressed;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
class InputEvent;
|
||||
class Shortcut;
|
||||
class Viewport;
|
||||
|
||||
class ViewPanner : public RefCounted {
|
||||
GDCLASS(ViewPanner, RefCounted);
|
||||
|
|
@ -69,6 +70,7 @@ private:
|
|||
Callable zoom_callback;
|
||||
|
||||
ControlScheme control_scheme = SCROLL_ZOOMS;
|
||||
Viewport *warped_panning_viewport = nullptr;
|
||||
|
||||
public:
|
||||
void set_callbacks(Callable p_pan_callback, Callable p_zoom_callback);
|
||||
|
|
@ -81,6 +83,7 @@ public:
|
|||
void set_pan_axis(PanAxis p_pan_axis);
|
||||
|
||||
void setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning);
|
||||
void setup_warped_panning(Viewport *p_viewport, bool p_allowed);
|
||||
|
||||
bool is_panning() const;
|
||||
void set_force_drag(bool p_force);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue