feat: modules moved and engine moved to submodule
This commit is contained in:
parent
dfb5e645cd
commit
c33d2130cc
5136 changed files with 225275 additions and 64485 deletions
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ASPECT_RATIO_CONTAINER_H
|
||||
#define ASPECT_RATIO_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -79,5 +78,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(AspectRatioContainer::StretchMode);
|
||||
VARIANT_ENUM_CAST(AspectRatioContainer::AlignmentMode);
|
||||
|
||||
#endif // ASPECT_RATIO_CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ void BaseButton::_unpress_group() {
|
|||
|
||||
if (toggle_mode && !button_group->is_allow_unpress()) {
|
||||
status.pressed = true;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
for (BaseButton *E : button_group->buttons) {
|
||||
|
|
@ -83,15 +84,66 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
|
||||
void BaseButton::_accessibility_action_click(const Variant &p_data) {
|
||||
if (toggle_mode) {
|
||||
status.pressed = !status.pressed;
|
||||
|
||||
if (status.pressed) {
|
||||
_unpress_group();
|
||||
if (button_group.is_valid()) {
|
||||
button_group->emit_signal(SceneStringName(pressed), this);
|
||||
}
|
||||
}
|
||||
|
||||
_toggled(status.pressed);
|
||||
_pressed();
|
||||
} else {
|
||||
_pressed();
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void BaseButton::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_BUTTON);
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_CLICK, callable_mp(this, &BaseButton::_accessibility_action_click));
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(ae, DisplayServer::AccessibilityFlags::FLAG_DISABLED, status.disabled);
|
||||
if (toggle_mode) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_checked(ae, status.pressed);
|
||||
}
|
||||
if (button_group.is_valid()) {
|
||||
for (const BaseButton *btn : button_group->buttons) {
|
||||
if (btn->is_part_of_edited_scene()) {
|
||||
continue;
|
||||
}
|
||||
DisplayServer::get_singleton()->accessibility_update_add_related_radio_group(ae, btn->get_accessibility_element());
|
||||
}
|
||||
}
|
||||
if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->has_valid_event()) {
|
||||
String text = atr(shortcut->get_name()) + " (" + shortcut->get_as_text() + ")";
|
||||
String tooltip = get_tooltip_text();
|
||||
if (!tooltip.is_empty() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
|
||||
text += "\n" + atr(tooltip);
|
||||
}
|
||||
DisplayServer::get_singleton()->accessibility_update_set_tooltip(ae, text);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_MOUSE_ENTER: {
|
||||
status.hovering = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_MOUSE_EXIT: {
|
||||
status.hovering = false;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
} break;
|
||||
|
||||
|
|
@ -175,6 +227,7 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
|
|||
}
|
||||
_toggled(status.pressed);
|
||||
_pressed();
|
||||
queue_accessibility_update();
|
||||
}
|
||||
} else {
|
||||
if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (!p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
|
||||
|
|
@ -214,6 +267,7 @@ void BaseButton::set_disabled(bool p_disabled) {
|
|||
status.press_attempt = false;
|
||||
status.pressing_inside = false;
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
}
|
||||
|
|
@ -247,7 +301,7 @@ void BaseButton::set_pressed_no_signal(bool p_pressed) {
|
|||
return;
|
||||
}
|
||||
status.pressed = p_pressed;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -303,6 +357,7 @@ void BaseButton::set_toggle_mode(bool p_on) {
|
|||
if (!p_on) {
|
||||
set_pressed(false);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
|
||||
toggle_mode = p_on;
|
||||
update_configuration_warnings();
|
||||
|
|
@ -313,7 +368,10 @@ bool BaseButton::is_toggle_mode() const {
|
|||
}
|
||||
|
||||
void BaseButton::set_shortcut_in_tooltip(bool p_on) {
|
||||
shortcut_in_tooltip = p_on;
|
||||
if (shortcut_in_tooltip != p_on) {
|
||||
shortcut_in_tooltip = p_on;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
}
|
||||
|
||||
bool BaseButton::is_shortcut_in_tooltip_enabled() const {
|
||||
|
|
@ -353,8 +411,11 @@ bool BaseButton::is_shortcut_feedback() const {
|
|||
}
|
||||
|
||||
void BaseButton::set_shortcut(const Ref<Shortcut> &p_shortcut) {
|
||||
shortcut = p_shortcut;
|
||||
set_process_shortcut_input(shortcut.is_valid());
|
||||
if (shortcut != p_shortcut) {
|
||||
shortcut = p_shortcut;
|
||||
set_process_shortcut_input(shortcut.is_valid());
|
||||
queue_accessibility_update();
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Shortcut> BaseButton::get_shortcut() const {
|
||||
|
|
@ -380,7 +441,7 @@ void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
_toggled(status.pressed);
|
||||
_pressed();
|
||||
|
||||
queue_accessibility_update();
|
||||
} else {
|
||||
_pressed();
|
||||
}
|
||||
|
|
@ -440,6 +501,7 @@ void BaseButton::set_button_group(const Ref<ButtonGroup> &p_group) {
|
|||
button_group->buttons.insert(this);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw(); //checkbox changes to radio if set a buttongroup
|
||||
update_configuration_warnings();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef BASE_BUTTON_H
|
||||
#define BASE_BUTTON_H
|
||||
#pragma once
|
||||
|
||||
#include "core/input/shortcut.h"
|
||||
#include "scene/gui/control.h"
|
||||
|
|
@ -87,6 +86,7 @@ protected:
|
|||
void _notification(int p_what);
|
||||
|
||||
bool _was_pressed_by_mouse() const;
|
||||
void _accessibility_action_click(const Variant &p_data);
|
||||
|
||||
GDVIRTUAL0(_pressed)
|
||||
GDVIRTUAL1(_toggled, bool)
|
||||
|
|
@ -165,5 +165,3 @@ public:
|
|||
bool is_allow_unpress();
|
||||
ButtonGroup();
|
||||
};
|
||||
|
||||
#endif // BASE_BUTTON_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef BOX_CONTAINER_H
|
||||
#define BOX_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -97,5 +96,3 @@ public:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(BoxContainer::AlignmentMode);
|
||||
|
||||
#endif // BOX_CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#include "button.h"
|
||||
|
||||
#include "scene/gui/dialogs.h"
|
||||
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
Size2 Button::get_minimum_size() const {
|
||||
|
|
@ -48,6 +50,10 @@ void Button::_set_internal_margin(Side p_side, float p_value) {
|
|||
void Button::_queue_update_size_cache() {
|
||||
}
|
||||
|
||||
String Button::_get_translated_text(const String &p_text) const {
|
||||
return atr(p_text);
|
||||
}
|
||||
|
||||
void Button::_update_theme_item_cache() {
|
||||
Control::_update_theme_item_cache();
|
||||
|
||||
|
|
@ -181,15 +187,31 @@ Ref<StyleBox> Button::_get_current_stylebox() const {
|
|||
|
||||
void Button::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
if (!xl_text.is_empty() && get_accessibility_name().is_empty()) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(ae, xl_text);
|
||||
} else if (!xl_text.is_empty() && !get_accessibility_name().is_empty() && get_accessibility_name() != xl_text) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(ae, get_accessibility_name() + ": " + xl_text);
|
||||
}
|
||||
AcceptDialog *dlg = Object::cast_to<AcceptDialog>(get_parent());
|
||||
if (dlg && dlg->get_ok_button() == this) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_DEFAULT_BUTTON);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
|
||||
queue_redraw();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
xl_text = atr(text);
|
||||
xl_text = _get_translated_text(text);
|
||||
_shape();
|
||||
|
||||
update_minimum_size();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
} break;
|
||||
|
||||
|
|
@ -566,7 +588,7 @@ void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) const {
|
|||
case TextServer::AUTOWRAP_OFF:
|
||||
break;
|
||||
}
|
||||
autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
|
||||
autowrap_flags = autowrap_flags | autowrap_flags_trim;
|
||||
p_paragraph->set_break_flags(autowrap_flags);
|
||||
p_paragraph->set_line_spacing(theme_cache.line_spacing);
|
||||
|
||||
|
|
@ -598,14 +620,17 @@ TextServer::OverrunBehavior Button::get_text_overrun_behavior() const {
|
|||
}
|
||||
|
||||
void Button::set_text(const String &p_text) {
|
||||
if (text != p_text) {
|
||||
text = p_text;
|
||||
xl_text = atr(text);
|
||||
_shape();
|
||||
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
const String translated_text = _get_translated_text(p_text);
|
||||
if (text == p_text && xl_text == translated_text) {
|
||||
return;
|
||||
}
|
||||
text = p_text;
|
||||
xl_text = translated_text;
|
||||
_shape();
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
}
|
||||
|
||||
String Button::get_text() const {
|
||||
|
|
@ -625,11 +650,25 @@ TextServer::AutowrapMode Button::get_autowrap_mode() const {
|
|||
return autowrap_mode;
|
||||
}
|
||||
|
||||
void Button::set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags) {
|
||||
if (autowrap_flags_trim != (p_flags & TextServer::BREAK_TRIM_MASK)) {
|
||||
autowrap_flags_trim = p_flags & TextServer::BREAK_TRIM_MASK;
|
||||
_shape();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
}
|
||||
}
|
||||
|
||||
BitField<TextServer::LineBreakFlag> Button::get_autowrap_trim_flags() const {
|
||||
return autowrap_flags_trim;
|
||||
}
|
||||
|
||||
void Button::set_text_direction(Control::TextDirection p_text_direction) {
|
||||
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
|
||||
if (text_direction != p_text_direction) {
|
||||
text_direction = p_text_direction;
|
||||
_shape();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -642,6 +681,7 @@ void Button::set_language(const String &p_language) {
|
|||
if (language != p_language) {
|
||||
language = p_language;
|
||||
_shape();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -719,6 +759,7 @@ bool Button::get_clip_text() const {
|
|||
void Button::set_text_alignment(HorizontalAlignment p_alignment) {
|
||||
if (alignment != p_alignment) {
|
||||
alignment = p_alignment;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -766,6 +807,8 @@ void Button::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &Button::get_text_overrun_behavior);
|
||||
ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Button::set_autowrap_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Button::get_autowrap_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_autowrap_trim_flags", "autowrap_trim_flags"), &Button::set_autowrap_trim_flags);
|
||||
ClassDB::bind_method(D_METHOD("get_autowrap_trim_flags"), &Button::get_autowrap_trim_flags);
|
||||
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Button::set_text_direction);
|
||||
ClassDB::bind_method(D_METHOD("get_text_direction"), &Button::get_text_direction);
|
||||
ClassDB::bind_method(D_METHOD("set_language", "language"), &Button::set_language);
|
||||
|
|
@ -791,8 +834,9 @@ void Button::_bind_methods() {
|
|||
|
||||
ADD_GROUP("Text Behavior", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_alignment", "get_text_alignment");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis (6+ Characters),Word Ellipsis (6+ Characters),Ellipsis (Always),Word Ellipsis (Always)"), "set_text_overrun_behavior", "get_text_overrun_behavior");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_trim_flags", PROPERTY_HINT_FLAGS, vformat("Trim Spaces After Break:%d,Trim Spaces Before Break:%d", TextServer::BREAK_TRIM_START_EDGE_SPACES, TextServer::BREAK_TRIM_END_EDGE_SPACES)), "set_autowrap_trim_flags", "get_autowrap_trim_flags");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
|
||||
|
||||
ADD_GROUP("Icon Behavior", "");
|
||||
|
|
@ -846,7 +890,7 @@ void Button::_bind_methods() {
|
|||
|
||||
Button::Button(const String &p_text) {
|
||||
text_buf.instantiate();
|
||||
text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES);
|
||||
text_buf->set_break_flags(TextServer::BREAK_MANDATORY | autowrap_flags_trim);
|
||||
set_mouse_filter(MOUSE_FILTER_STOP);
|
||||
|
||||
set_text(p_text);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef BUTTON_H
|
||||
#define BUTTON_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/base_button.h"
|
||||
#include "scene/resources/text_paragraph.h"
|
||||
|
|
@ -46,6 +45,7 @@ private:
|
|||
String language;
|
||||
TextDirection text_direction = TEXT_DIRECTION_AUTO;
|
||||
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
|
||||
BitField<TextServer::LineBreakFlag> autowrap_flags_trim = TextServer::BREAK_TRIM_END_EDGE_SPACES;
|
||||
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
|
||||
|
||||
Ref<Texture2D> icon;
|
||||
|
|
@ -111,6 +111,7 @@ protected:
|
|||
|
||||
void _set_internal_margin(Side p_side, float p_value);
|
||||
virtual void _queue_update_size_cache();
|
||||
virtual String _get_translated_text(const String &p_text) const;
|
||||
|
||||
Size2 _fit_icon_size(const Size2 &p_size) const;
|
||||
Ref<StyleBox> _get_current_stylebox() const;
|
||||
|
|
@ -132,6 +133,9 @@ public:
|
|||
void set_autowrap_mode(TextServer::AutowrapMode p_mode);
|
||||
TextServer::AutowrapMode get_autowrap_mode() const;
|
||||
|
||||
void set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags);
|
||||
BitField<TextServer::LineBreakFlag> get_autowrap_trim_flags() const;
|
||||
|
||||
void set_text_direction(TextDirection p_text_direction);
|
||||
TextDirection get_text_direction() const;
|
||||
|
||||
|
|
@ -161,5 +165,3 @@ public:
|
|||
Button(const String &p_text = String());
|
||||
~Button();
|
||||
};
|
||||
|
||||
#endif // BUTTON_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CENTER_CONTAINER_H
|
||||
#define CENTER_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -53,5 +52,3 @@ public:
|
|||
|
||||
CenterContainer();
|
||||
};
|
||||
|
||||
#endif // CENTER_CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -81,6 +81,17 @@ Size2 CheckBox::get_minimum_size() const {
|
|||
|
||||
void CheckBox::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
if (is_radio()) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_RADIO_BUTTON);
|
||||
} else {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_CHECK_BOX);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
|
|
@ -126,15 +137,15 @@ void CheckBox::_notification(int p_what) {
|
|||
ofs.y = int((get_size().height - get_icon_size().height) / 2) + theme_cache.check_v_offset;
|
||||
|
||||
if (is_pressed()) {
|
||||
on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size())));
|
||||
on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size())), false, theme_cache.checkbox_checked_color);
|
||||
} else {
|
||||
off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size())));
|
||||
off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size())), false, theme_cache.checkbox_unchecked_color);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckBox::is_radio() {
|
||||
bool CheckBox::is_radio() const {
|
||||
return get_button_group().is_valid();
|
||||
}
|
||||
|
||||
|
|
@ -151,6 +162,9 @@ void CheckBox::_bind_methods() {
|
|||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CheckBox, unchecked_disabled);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CheckBox, radio_checked_disabled);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CheckBox, radio_unchecked_disabled);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CheckBox, checkbox_checked_color);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CheckBox, checkbox_unchecked_color);
|
||||
}
|
||||
|
||||
CheckBox::CheckBox(const String &p_text) :
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CHECK_BOX_H
|
||||
#define CHECK_BOX_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/button.h"
|
||||
|
||||
|
|
@ -49,6 +48,9 @@ class CheckBox : public Button {
|
|||
Ref<Texture2D> unchecked_disabled;
|
||||
Ref<Texture2D> radio_checked_disabled;
|
||||
Ref<Texture2D> radio_unchecked_disabled;
|
||||
|
||||
Color checkbox_checked_color;
|
||||
Color checkbox_unchecked_color;
|
||||
} theme_cache;
|
||||
|
||||
protected:
|
||||
|
|
@ -58,11 +60,9 @@ protected:
|
|||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
bool is_radio();
|
||||
bool is_radio() const;
|
||||
|
||||
public:
|
||||
CheckBox(const String &p_text = String());
|
||||
~CheckBox();
|
||||
};
|
||||
|
||||
#endif // CHECK_BOX_H
|
||||
|
|
|
|||
|
|
@ -85,6 +85,13 @@ Size2 CheckButton::get_minimum_size() const {
|
|||
|
||||
void CheckButton::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_CHECK_BUTTON);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
|
|
@ -133,9 +140,9 @@ void CheckButton::_notification(int p_what) {
|
|||
ofs.y = (get_size().height - tex_size.height) / 2 + theme_cache.check_v_offset;
|
||||
|
||||
if (is_pressed()) {
|
||||
on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size())));
|
||||
on_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(on_tex->get_size())), false, theme_cache.button_checked_color);
|
||||
} else {
|
||||
off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size())));
|
||||
off_tex->draw_rect(ci, Rect2(ofs, _fit_icon_size(off_tex->get_size())), false, theme_cache.button_unchecked_color);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
|
@ -154,6 +161,9 @@ void CheckButton::_bind_methods() {
|
|||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CheckButton, unchecked_mirrored);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CheckButton, checked_disabled_mirrored);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, CheckButton, unchecked_disabled_mirrored);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CheckButton, button_checked_color);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, CheckButton, button_unchecked_color);
|
||||
}
|
||||
|
||||
CheckButton::CheckButton(const String &p_text) :
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CHECK_BUTTON_H
|
||||
#define CHECK_BUTTON_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/button.h"
|
||||
|
||||
|
|
@ -49,6 +48,9 @@ class CheckButton : public Button {
|
|||
Ref<Texture2D> unchecked_mirrored;
|
||||
Ref<Texture2D> checked_disabled_mirrored;
|
||||
Ref<Texture2D> unchecked_disabled_mirrored;
|
||||
|
||||
Color button_checked_color;
|
||||
Color button_unchecked_color;
|
||||
} theme_cache;
|
||||
|
||||
protected:
|
||||
|
|
@ -62,5 +64,3 @@ public:
|
|||
CheckButton(const String &p_text = String());
|
||||
~CheckButton();
|
||||
};
|
||||
|
||||
#endif // CHECK_BUTTON_H
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ void CodeEdit::_notification(int p_what) {
|
|||
|
||||
Point2 round_ofs = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(0, theme_cache.font->get_ascent(theme_cache.font_size) + font_height * i + yofs);
|
||||
round_ofs = round_ofs.round();
|
||||
draw_string(theme_cache.font, round_ofs, line.replace(String::chr(0xFFFF), ""), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size, theme_cache.code_hint_color);
|
||||
draw_string(theme_cache.font, round_ofs, line.remove_char(0xFFFF), HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size, theme_cache.code_hint_color);
|
||||
if (end > 0) {
|
||||
// Draw an underline for the currently edited function parameter.
|
||||
const Vector2 b = hint_ofs + theme_cache.code_hint_style->get_offset() + Vector2(begin, font_height + font_height * i + yofs);
|
||||
|
|
@ -963,7 +963,7 @@ void CodeEdit::indent_lines() {
|
|||
for (Point2i line_range : line_ranges) {
|
||||
for (int i = line_range.x; i <= line_range.y; i++) {
|
||||
const String line_text = get_line(i);
|
||||
if (line_text.size() == 0) {
|
||||
if (line_text.is_empty()) {
|
||||
// Ignore empty lines.
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1247,9 +1247,8 @@ void CodeEdit::add_auto_brace_completion_pair(const String &p_open_key, const St
|
|||
void CodeEdit::set_auto_brace_completion_pairs(const Dictionary &p_auto_brace_completion_pairs) {
|
||||
auto_brace_completion_pairs.clear();
|
||||
|
||||
Array keys = p_auto_brace_completion_pairs.keys();
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
add_auto_brace_completion_pair(keys[i], p_auto_brace_completion_pairs[keys[i]]);
|
||||
for (const KeyValue<Variant, Variant> &kv : p_auto_brace_completion_pairs) {
|
||||
add_auto_brace_completion_pair(kv.key, kv.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1599,7 +1598,7 @@ bool CodeEdit::can_fold_line(int p_line) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (p_line + 1 >= get_line_count() || get_line(p_line).strip_edges().size() == 0) {
|
||||
if (p_line + 1 >= get_line_count() || get_line(p_line).strip_edges().is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1645,12 +1644,12 @@ bool CodeEdit::can_fold_line(int p_line) const {
|
|||
if (delimiter_end_line == p_line) {
|
||||
/* Check we are the start of the block. */
|
||||
if (p_line - 1 >= 0) {
|
||||
if ((in_string != -1 && is_in_string(p_line - 1) != -1) || (in_comment != -1 && is_in_comment(p_line - 1) != -1)) {
|
||||
if ((in_string != -1 && is_in_string(p_line - 1) != -1) || (in_comment != -1 && is_in_comment(p_line - 1) != -1 && !is_line_code_region_start(p_line - 1) && !is_line_code_region_end(p_line - 1))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* Check it continues for at least one line. */
|
||||
return ((in_string != -1 && is_in_string(p_line + 1) != -1) || (in_comment != -1 && is_in_comment(p_line + 1) != -1));
|
||||
return ((in_string != -1 && is_in_string(p_line + 1) != -1) || (in_comment != -1 && is_in_comment(p_line + 1) != -1 && !is_line_code_region_start(p_line + 1) && !is_line_code_region_end(p_line + 1)));
|
||||
}
|
||||
return ((in_string != -1 && is_in_string(delimiter_end_line) != -1) || (in_comment != -1 && is_in_comment(delimiter_end_line) != -1));
|
||||
}
|
||||
|
|
@ -1658,7 +1657,7 @@ bool CodeEdit::can_fold_line(int p_line) const {
|
|||
/* Otherwise check indent levels. */
|
||||
int start_indent = get_indent_level(p_line);
|
||||
for (int i = p_line + 1; i < get_line_count(); i++) {
|
||||
if (is_in_string(i) != -1 || is_in_comment(i) != -1 || get_line(i).strip_edges().size() == 0) {
|
||||
if (is_in_string(i) != -1 || is_in_comment(i) != -1 || get_line(i).strip_edges().is_empty()) {
|
||||
continue;
|
||||
}
|
||||
return (get_indent_level(i) > start_indent);
|
||||
|
|
@ -1705,13 +1704,17 @@ void CodeEdit::fold_line(int p_line) {
|
|||
if ((in_string != -1 && is_in_string(i) == -1) || (in_comment != -1 && is_in_comment(i) == -1)) {
|
||||
break;
|
||||
}
|
||||
if (in_comment != -1 && (is_line_code_region_start(i) || is_line_code_region_end(i))) {
|
||||
// A code region tag should split a comment block, ending it early.
|
||||
break;
|
||||
}
|
||||
end_line = i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int start_indent = get_indent_level(p_line);
|
||||
for (int i = p_line + 1; i <= line_count; i++) {
|
||||
if (get_line(i).strip_edges().size() == 0) {
|
||||
if (get_line(i).strip_edges().is_empty()) {
|
||||
continue;
|
||||
}
|
||||
if (get_indent_level(i) > start_indent) {
|
||||
|
|
@ -1872,7 +1875,8 @@ bool CodeEdit::is_line_code_region_start(int p_line) const {
|
|||
if (is_in_string(p_line) != -1) {
|
||||
return false;
|
||||
}
|
||||
return get_line(p_line).strip_edges().begins_with(code_region_start_string);
|
||||
Vector<String> split = get_line(p_line).strip_edges().split_spaces(1);
|
||||
return split.size() > 0 && split[0] == code_region_start_string;
|
||||
}
|
||||
|
||||
bool CodeEdit::is_line_code_region_end(int p_line) const {
|
||||
|
|
@ -1883,7 +1887,8 @@ bool CodeEdit::is_line_code_region_end(int p_line) const {
|
|||
if (is_in_string(p_line) != -1) {
|
||||
return false;
|
||||
}
|
||||
return get_line(p_line).strip_edges().begins_with(code_region_end_string);
|
||||
Vector<String> split = get_line(p_line).strip_edges().split_spaces(1);
|
||||
return split.size() > 0 && split[0] == code_region_end_string;
|
||||
}
|
||||
|
||||
/* Delimiters */
|
||||
|
|
@ -1956,7 +1961,7 @@ String CodeEdit::get_delimiter_end_key(int p_delimiter_idx) const {
|
|||
}
|
||||
|
||||
Point2 CodeEdit::get_delimiter_start_position(int p_line, int p_column) const {
|
||||
if (delimiters.size() == 0) {
|
||||
if (delimiters.is_empty()) {
|
||||
return Point2(-1, -1);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_line, get_line_count(), Point2(-1, -1));
|
||||
|
|
@ -2007,7 +2012,7 @@ Point2 CodeEdit::get_delimiter_start_position(int p_line, int p_column) const {
|
|||
}
|
||||
|
||||
Point2 CodeEdit::get_delimiter_end_position(int p_line, int p_column) const {
|
||||
if (delimiters.size() == 0) {
|
||||
if (delimiters.is_empty()) {
|
||||
return Point2(-1, -1);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_line, get_line_count(), Point2(-1, -1));
|
||||
|
|
@ -2112,7 +2117,7 @@ String CodeEdit::get_text_for_code_completion() const {
|
|||
completion_text += line.substr(0, get_caret_column());
|
||||
/* Not unicode, represents the caret. */
|
||||
completion_text += String::chr(0xFFFF);
|
||||
completion_text += line.substr(get_caret_column(), line.size());
|
||||
completion_text += line.substr(get_caret_column());
|
||||
} else {
|
||||
completion_text += line;
|
||||
}
|
||||
|
|
@ -2411,7 +2416,7 @@ String CodeEdit::get_text_with_cursor_char(int p_line, int p_column) const {
|
|||
result += line_text.substr(0, p_column);
|
||||
/* Not unicode, represents the cursor. */
|
||||
result += String::chr(0xFFFF);
|
||||
result += line_text.substr(p_column, line_text.size());
|
||||
result += line_text.substr(p_column);
|
||||
} else {
|
||||
result += line_text;
|
||||
}
|
||||
|
|
@ -3058,7 +3063,7 @@ void CodeEdit::_update_code_region_tags() {
|
|||
|
||||
/* Delimiters */
|
||||
void CodeEdit::_update_delimiter_cache(int p_from_line, int p_to_line) {
|
||||
if (delimiters.size() == 0) {
|
||||
if (delimiters.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -3208,7 +3213,7 @@ void CodeEdit::_update_delimiter_cache(int p_from_line, int p_to_line) {
|
|||
}
|
||||
|
||||
int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) const {
|
||||
if (delimiters.size() == 0 || p_line >= delimiter_cache.size()) {
|
||||
if (delimiters.is_empty() || p_line >= delimiter_cache.size()) {
|
||||
return -1;
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_line, get_line_count(), 0);
|
||||
|
|
@ -3348,8 +3353,8 @@ void CodeEdit::_set_delimiters(const TypedArray<String> &p_delimiters, Delimiter
|
|||
continue;
|
||||
}
|
||||
|
||||
const String start_key = key.get_slice(" ", 0);
|
||||
const String end_key = key.get_slice_count(" ") > 1 ? key.get_slice(" ", 1) : String();
|
||||
const String start_key = key.get_slicec(' ', 0);
|
||||
const String end_key = key.get_slice_count(" ") > 1 ? key.get_slicec(' ', 1) : String();
|
||||
|
||||
_add_delimiter(start_key, end_key, end_key.is_empty(), p_type);
|
||||
}
|
||||
|
|
@ -3422,7 +3427,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
|
|||
GDVIRTUAL_CALL(_filter_code_completion_candidates, completion_options_sources, completion_options);
|
||||
|
||||
/* No options to complete, cancel. */
|
||||
if (completion_options.size() == 0) {
|
||||
if (completion_options.is_empty()) {
|
||||
cancel_code_completion();
|
||||
return;
|
||||
}
|
||||
|
|
@ -3654,7 +3659,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
|
|||
}
|
||||
|
||||
/* No options to complete, cancel. */
|
||||
if (code_completion_options_new.size() == 0) {
|
||||
if (code_completion_options_new.is_empty()) {
|
||||
cancel_code_completion();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CODE_EDIT_H
|
||||
#define CODE_EDIT_H
|
||||
#pragma once
|
||||
|
||||
#include "core/object/script_language.h"
|
||||
#include "scene/gui/text_edit.h"
|
||||
|
|
@ -527,5 +526,3 @@ VARIANT_ENUM_CAST(CodeEdit::CodeCompletionLocation);
|
|||
struct CodeCompletionOptionCompare {
|
||||
_FORCE_INLINE_ bool operator()(const ScriptLanguage::CodeCompletionOption &l, const ScriptLanguage::CodeCompletionOption &r) const;
|
||||
};
|
||||
|
||||
#endif // CODE_EDIT_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COLOR_MODE_H
|
||||
#define COLOR_MODE_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/color_picker.h"
|
||||
|
||||
|
|
@ -54,7 +53,6 @@ public:
|
|||
|
||||
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() {}
|
||||
|
|
@ -145,12 +143,7 @@ public:
|
|||
virtual void _value_changed() override;
|
||||
|
||||
virtual void slider_draw(int p_which) override;
|
||||
virtual ColorPicker::PickerShapeType get_shape_override() const override { return ColorPicker::SHAPE_OKHSL_CIRCLE; }
|
||||
|
||||
ColorModeOKHSL(ColorPicker *p_color_picker) :
|
||||
ColorMode(p_color_picker) {}
|
||||
|
||||
~ColorModeOKHSL() {}
|
||||
};
|
||||
|
||||
#endif // COLOR_MODE_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COLOR_PICKER_H
|
||||
#define COLOR_PICKER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
|
|
@ -58,6 +57,7 @@ class ColorPresetButton : public BaseButton {
|
|||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> foreground_style;
|
||||
Ref<StyleBox> focus_style;
|
||||
|
||||
Ref<Texture2D> background_icon;
|
||||
Ref<Texture2D> overbright_indicator;
|
||||
|
|
@ -79,6 +79,13 @@ class ColorPicker : public VBoxContainer {
|
|||
GDCLASS(ColorPicker, VBoxContainer);
|
||||
|
||||
// These classes poke into theme items for their internal logic.
|
||||
friend class ColorPickerShape;
|
||||
friend class ColorPickerShapeRectangle;
|
||||
friend class ColorPickerShapeWheel;
|
||||
friend class ColorPickerShapeCircle;
|
||||
friend class ColorPickerShapeVHSCircle;
|
||||
friend class ColorPickerShapeOKHSLCircle;
|
||||
|
||||
friend class ColorModeRGB;
|
||||
friend class ColorModeHSV;
|
||||
friend class ColorModeRAW;
|
||||
|
|
@ -126,12 +133,18 @@ private:
|
|||
#endif
|
||||
|
||||
int current_slider_count = SLIDER_COUNT;
|
||||
static const int MODE_BUTTON_COUNT = 3;
|
||||
const float WHEEL_RADIUS = 0.42;
|
||||
Vector2i circle_keyboard_joypad_picker_cursor_position;
|
||||
|
||||
const float DEFAULT_GAMEPAD_EVENT_DELAY_MS = 1.0 / 2;
|
||||
const float GAMEPAD_EVENT_REPEAT_RATE_MS = 1.0 / 30;
|
||||
float gamepad_event_delay_ms = DEFAULT_GAMEPAD_EVENT_DELAY_MS;
|
||||
|
||||
static constexpr int MODE_BUTTON_COUNT = 3;
|
||||
|
||||
bool slider_theme_modified = true;
|
||||
|
||||
Vector<ColorMode *> modes;
|
||||
LocalVector<ColorMode *> modes;
|
||||
LocalVector<ColorPickerShape *> shapes;
|
||||
|
||||
Popup *picker_window = nullptr;
|
||||
TextureRect *picker_texture_zoom = nullptr;
|
||||
|
|
@ -148,14 +161,7 @@ private:
|
|||
PopupMenu *options_menu = nullptr;
|
||||
|
||||
MarginContainer *internal_margin = nullptr;
|
||||
Control *uv_edit = nullptr;
|
||||
Control *w_edit = nullptr;
|
||||
AspectRatioContainer *wheel_edit = nullptr;
|
||||
MarginContainer *wheel_margin = nullptr;
|
||||
Ref<ShaderMaterial> wheel_mat;
|
||||
Ref<ShaderMaterial> circle_mat;
|
||||
Control *wheel = nullptr;
|
||||
Control *wheel_uv = nullptr;
|
||||
HBoxContainer *shape_container = nullptr;
|
||||
TextureRect *sample = nullptr;
|
||||
VBoxContainer *swatches_vbc = nullptr;
|
||||
GridContainer *preset_container = nullptr;
|
||||
|
|
@ -179,6 +185,7 @@ 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;
|
||||
|
|
@ -251,6 +258,9 @@ private:
|
|||
|
||||
bool center_slider_grabbers = true;
|
||||
|
||||
Ref<StyleBox> picker_focus_rectangle;
|
||||
Ref<StyleBox> picker_focus_circle;
|
||||
Color focused_not_editing_cursor_color;
|
||||
Ref<Texture2D> menu_option;
|
||||
Ref<Texture2D> screen_picker;
|
||||
Ref<Texture2D> expanded_arrow;
|
||||
|
|
@ -264,6 +274,7 @@ private:
|
|||
Ref<Texture2D> bar_arrow;
|
||||
Ref<Texture2D> sample_bg;
|
||||
Ref<Texture2D> sample_revert;
|
||||
Ref<StyleBox> sample_focus;
|
||||
Ref<Texture2D> overbright_indicator;
|
||||
Ref<Texture2D> picker_cursor;
|
||||
Ref<Texture2D> picker_cursor_bg;
|
||||
|
|
@ -278,7 +289,6 @@ private:
|
|||
void _copy_color_to_hsv();
|
||||
void _copy_hsv_to_color();
|
||||
|
||||
PickerShapeType _get_actual_shape() const;
|
||||
void create_slider(GridContainer *gc, int idx);
|
||||
void _reset_sliders_theme();
|
||||
void _html_submitted(const String &p_html);
|
||||
|
|
@ -291,11 +301,8 @@ private:
|
|||
void _text_type_toggled();
|
||||
void _sample_input(const Ref<InputEvent> &p_event);
|
||||
void _sample_draw();
|
||||
void _hsv_draw(int p_which, Control *c);
|
||||
void _slider_draw(int p_which);
|
||||
|
||||
void _uv_input(const Ref<InputEvent> &p_event, Control *c);
|
||||
void _w_input(const Ref<InputEvent> &p_event);
|
||||
void _slider_or_spin_input(const Ref<InputEvent> &p_event);
|
||||
void _line_edit_input(const Ref<InputEvent> &p_event);
|
||||
void _preset_input(const Ref<InputEvent> &p_event, const Color &p_color);
|
||||
|
|
@ -308,6 +315,12 @@ private:
|
|||
void _pick_finished();
|
||||
void _update_menu_items();
|
||||
void _options_menu_cbk(int p_which);
|
||||
void _block_input_on_popup_show();
|
||||
void _enable_input_on_popup_hide();
|
||||
|
||||
// Native color picking.
|
||||
void _pick_button_pressed_native();
|
||||
void _native_cb(bool p_status, const Color &p_color);
|
||||
|
||||
// Legacy color picking.
|
||||
void _pick_button_pressed_legacy();
|
||||
|
|
@ -347,6 +360,7 @@ public:
|
|||
static void finish_shaders();
|
||||
|
||||
void add_mode(ColorMode *p_mode);
|
||||
void add_shape(ColorPickerShape *p_shape);
|
||||
|
||||
void set_edit_alpha(bool p_show);
|
||||
bool is_editing_alpha() const;
|
||||
|
|
@ -405,6 +419,7 @@ public:
|
|||
bool is_hex_visible() const;
|
||||
|
||||
void set_focus_on_line_edit();
|
||||
void set_focus_on_picker_shape();
|
||||
|
||||
ColorPicker();
|
||||
~ColorPicker();
|
||||
|
|
@ -460,5 +475,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(ColorPicker::PickerShapeType);
|
||||
VARIANT_ENUM_CAST(ColorPicker::ColorModeType);
|
||||
|
||||
#endif // COLOR_PICKER_H
|
||||
|
|
|
|||
771
engine/scene/gui/color_picker_shape.cpp
Normal file
771
engine/scene/gui/color_picker_shape.cpp
Normal file
|
|
@ -0,0 +1,771 @@
|
|||
/**************************************************************************/
|
||||
/* color_picker_shape.cpp */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "color_picker_shape.h"
|
||||
|
||||
#include "scene/gui/margin_container.h"
|
||||
|
||||
void ColorPickerShape::_emit_color_changed() {
|
||||
color_picker->emit_signal(SNAME("color_changed"), color_picker->color);
|
||||
}
|
||||
|
||||
bool ColorPickerShape::can_handle(const Ref<InputEvent> &p_event, Vector2 &r_position, bool *r_is_click) {
|
||||
Ref<InputEventMouseButton> mb = p_event;
|
||||
if (mb.is_valid()) {
|
||||
if (mb->get_button_index() != MouseButton::LEFT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r_is_click) {
|
||||
*r_is_click = true;
|
||||
}
|
||||
|
||||
if (mb->is_pressed()) {
|
||||
is_dragging = true;
|
||||
r_position = mb->get_position();
|
||||
return true;
|
||||
} else {
|
||||
_emit_color_changed();
|
||||
color_picker->add_recent_preset(color_picker->color);
|
||||
is_dragging = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
if (is_dragging && mm.is_valid()) {
|
||||
r_position = mm->get_position();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColorPickerShape::apply_color() {
|
||||
color_picker->_copy_hsv_to_color();
|
||||
color_picker->last_color = color_picker->color;
|
||||
color_picker->set_pick_color(color_picker->color);
|
||||
|
||||
if (!color_picker->deferred_mode_enabled) {
|
||||
_emit_color_changed();
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPickerShape::cancel_event() {
|
||||
is_dragging = false;
|
||||
}
|
||||
|
||||
void ColorPickerShape::draw_focus_rect(Control *p_control, const Rect2 &p_rect) {
|
||||
if (!p_control->has_focus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rect2 focus_rect;
|
||||
if (p_rect.has_area()) {
|
||||
focus_rect = p_rect;
|
||||
} else {
|
||||
focus_rect = Rect2(Vector2(), p_control->get_size());
|
||||
}
|
||||
|
||||
const RID ci = p_control->get_canvas_item();
|
||||
if (!cursor_editing) {
|
||||
RenderingServer::get_singleton()->canvas_item_add_rect(ci, focus_rect, color_picker->theme_cache.focused_not_editing_cursor_color);
|
||||
}
|
||||
color_picker->theme_cache.picker_focus_rectangle->draw(ci, focus_rect);
|
||||
}
|
||||
|
||||
void ColorPickerShape::draw_focus_circle(Control *p_control) {
|
||||
if (!p_control->has_focus()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Rect2 focus_rect(Vector2(), p_control->get_size());
|
||||
const RID ci = p_control->get_canvas_item();
|
||||
if (!cursor_editing) {
|
||||
RenderingServer::get_singleton()->canvas_item_add_circle(ci, focus_rect.get_center(), focus_rect.get_size().y * 0.5, color_picker->theme_cache.focused_not_editing_cursor_color);
|
||||
}
|
||||
color_picker->theme_cache.picker_focus_circle->draw(ci, focus_rect);
|
||||
}
|
||||
|
||||
void ColorPickerShape::draw_sv_square(Control *p_control, const Rect2 &p_square, bool p_draw_focus) {
|
||||
const Vector2 end = p_square.get_end();
|
||||
PackedVector2Array points = {
|
||||
p_square.position,
|
||||
Vector2(end.x, p_square.position.y),
|
||||
end,
|
||||
Vector2(p_square.position.x, end.y),
|
||||
};
|
||||
|
||||
Color color1 = color_picker->color;
|
||||
color1.set_hsv(color_picker->h, 1, 1);
|
||||
Color color2 = color1;
|
||||
color2.set_hsv(color_picker->h, 1, 0);
|
||||
|
||||
PackedColorArray colors = {
|
||||
Color(1, 1, 1, 1),
|
||||
Color(1, 1, 1, 1),
|
||||
Color(0, 0, 0, 1),
|
||||
Color(0, 0, 0, 1)
|
||||
};
|
||||
p_control->draw_polygon(points, colors);
|
||||
|
||||
colors = {
|
||||
Color(color1, 0),
|
||||
Color(color1, 1),
|
||||
Color(color2, 1),
|
||||
Color(color2, 0)
|
||||
};
|
||||
p_control->draw_polygon(points, colors);
|
||||
|
||||
Vector2 cursor_pos;
|
||||
cursor_pos.x = CLAMP(p_square.position.x + p_square.size.x * color_picker->s, p_square.position.x, end.x);
|
||||
cursor_pos.y = CLAMP(p_square.position.y + p_square.size.y * (1.0 - color_picker->v), p_square.position.y, end.y);
|
||||
|
||||
if (p_draw_focus) {
|
||||
draw_focus_rect(p_control, p_square);
|
||||
}
|
||||
draw_cursor(p_control, cursor_pos);
|
||||
}
|
||||
|
||||
void ColorPickerShape::draw_cursor(Control *p_control, const Vector2 &p_center, bool p_draw_bg) {
|
||||
const Vector2 position = p_center - color_picker->theme_cache.picker_cursor->get_size() * 0.5;
|
||||
if (p_draw_bg) {
|
||||
p_control->draw_texture(color_picker->theme_cache.picker_cursor_bg, position, Color(color_picker->color, 1.0));
|
||||
}
|
||||
p_control->draw_texture(color_picker->theme_cache.picker_cursor, position);
|
||||
}
|
||||
|
||||
void ColorPickerShape::draw_circle_cursor(Control *p_control, float p_hue) {
|
||||
const Vector2 center = p_control->get_size() * 0.5;
|
||||
const Vector2 cursor_pos(
|
||||
center.x + (center.x * Math::cos(p_hue * Math::TAU) * color_picker->s),
|
||||
center.y + (center.y * Math::sin(p_hue * Math::TAU) * color_picker->s));
|
||||
|
||||
draw_cursor(p_control, cursor_pos);
|
||||
}
|
||||
|
||||
void ColorPickerShape::connect_shape_focus(Control *p_shape) {
|
||||
p_shape->set_focus_mode(Control::FOCUS_ALL);
|
||||
p_shape->connect(SceneStringName(focus_entered), callable_mp(this, &ColorPickerShape::shape_focus_entered));
|
||||
p_shape->connect(SceneStringName(focus_exited), callable_mp(this, &ColorPickerShape::shape_focus_exited));
|
||||
}
|
||||
|
||||
void ColorPickerShape::shape_focus_entered() {
|
||||
Input *input = Input::get_singleton();
|
||||
if (!(input->is_action_pressed("ui_up") || input->is_action_pressed("ui_down") || input->is_action_pressed("ui_left") || input->is_action_pressed("ui_right"))) {
|
||||
cursor_editing = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPickerShape::shape_focus_exited() {
|
||||
cursor_editing = false;
|
||||
}
|
||||
|
||||
void ColorPickerShape::handle_cursor_editing(const Ref<InputEvent> &p_event, Control *p_control) {
|
||||
if (p_event->is_action_pressed("ui_accept", false, true)) {
|
||||
cursor_editing = !cursor_editing;
|
||||
p_control->queue_redraw();
|
||||
color_picker->accept_event();
|
||||
}
|
||||
|
||||
if (cursor_editing && p_event->is_action_pressed("ui_cancel", false, true)) {
|
||||
cursor_editing = false;
|
||||
p_control->queue_redraw();
|
||||
color_picker->accept_event();
|
||||
}
|
||||
|
||||
if (!cursor_editing) {
|
||||
return;
|
||||
}
|
||||
|
||||
Input *input = Input::get_singleton();
|
||||
bool is_joypad_event = Object::cast_to<InputEventJoypadMotion>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr());
|
||||
|
||||
if (p_event->is_action_pressed("ui_left", true) || p_event->is_action_pressed("ui_right", true) || p_event->is_action_pressed("ui_up", true) || p_event->is_action_pressed("ui_down", true)) {
|
||||
if (is_joypad_event) {
|
||||
if (color_picker->is_processing_internal()) {
|
||||
color_picker->accept_event();
|
||||
return;
|
||||
}
|
||||
color_picker->set_process_internal(true);
|
||||
}
|
||||
|
||||
Vector2 color_change_vector = Vector2(
|
||||
input->is_action_pressed("ui_right") - input->is_action_pressed("ui_left"),
|
||||
input->is_action_pressed("ui_down") - input->is_action_pressed("ui_up"));
|
||||
update_cursor(color_change_vector, p_event->is_echo());
|
||||
color_picker->accept_event();
|
||||
}
|
||||
}
|
||||
|
||||
int ColorPickerShape::get_edge_h_change(const Vector2 &p_color_change_vector) {
|
||||
int h_change = 0;
|
||||
|
||||
if (color_picker->h > 0 && color_picker->h < 0.5) {
|
||||
h_change -= p_color_change_vector.x;
|
||||
} else if (color_picker->h > 0.5 && color_picker->h < 1) {
|
||||
h_change += p_color_change_vector.x;
|
||||
}
|
||||
|
||||
if (color_picker->h > 0.25 && color_picker->h < 0.75) {
|
||||
h_change -= p_color_change_vector.y;
|
||||
} else if (color_picker->h < 0.25 || color_picker->h > 0.75) {
|
||||
h_change += p_color_change_vector.y;
|
||||
}
|
||||
return h_change;
|
||||
}
|
||||
|
||||
float ColorPickerShape::get_h_on_circle_edge(const Vector2 &p_color_change_vector) {
|
||||
int h_change = get_edge_h_change(p_color_change_vector);
|
||||
|
||||
float target_h = Math::wrapf(color_picker->h + h_change / 360.0, 0, 1);
|
||||
int current_quarter = color_picker->h * 4;
|
||||
int future_quarter = target_h * 4;
|
||||
if (p_color_change_vector.y > 0 && ((future_quarter == 0 && current_quarter == 1) || (future_quarter == 1 && current_quarter == 0))) {
|
||||
target_h = 0.25f;
|
||||
} else if (p_color_change_vector.y < 0 && ((future_quarter == 2 && current_quarter == 3) || (future_quarter == 3 && current_quarter == 2))) {
|
||||
target_h = 0.75f;
|
||||
} else if (p_color_change_vector.x < 0 && ((future_quarter == 1 && current_quarter == 2) || (future_quarter == 2 && current_quarter == 1))) {
|
||||
target_h = 0.5f;
|
||||
} else if (p_color_change_vector.x > 0 && ((future_quarter == 3 && current_quarter == 0) || (future_quarter == 0 && current_quarter == 3))) {
|
||||
target_h = 0;
|
||||
}
|
||||
return target_h;
|
||||
}
|
||||
|
||||
void ColorPickerShape::initialize_controls() {
|
||||
_initialize_controls();
|
||||
update_theme();
|
||||
is_initialized = true;
|
||||
}
|
||||
|
||||
void ColorPickerShape::update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
|
||||
if (p_color_change_vector.is_zero_approx()) {
|
||||
echo_multiplier = 1.0;
|
||||
} else {
|
||||
echo_multiplier = p_is_echo ? CLAMP(echo_multiplier * 1.1, 1, 25) : 1;
|
||||
_update_cursor(p_color_change_vector * echo_multiplier, p_is_echo);
|
||||
apply_color();
|
||||
}
|
||||
}
|
||||
|
||||
ColorPickerShape::ColorPickerShape(ColorPicker *p_color_picker) {
|
||||
color_picker = p_color_picker;
|
||||
}
|
||||
|
||||
void ColorPickerShapeRectangle::_sv_square_input(const Ref<InputEvent> &p_event) {
|
||||
handle_cursor_editing(p_event, sv_square);
|
||||
|
||||
Vector2 event_position;
|
||||
if (!can_handle(p_event, event_position)) {
|
||||
return;
|
||||
}
|
||||
event_position = (event_position / sv_square->get_size()).clampf(0.0, 1.0);
|
||||
|
||||
color_picker->s = event_position.x;
|
||||
color_picker->v = 1.0 - event_position.y;
|
||||
|
||||
apply_color();
|
||||
}
|
||||
|
||||
void ColorPickerShapeRectangle::_hue_slider_input(const Ref<InputEvent> &p_event) {
|
||||
handle_cursor_editing(p_event, hue_slider);
|
||||
|
||||
Vector2 event_position;
|
||||
if (!can_handle(p_event, event_position)) {
|
||||
return;
|
||||
}
|
||||
color_picker->h = CLAMP(event_position.y / hue_slider->get_size().y, 0.0, 1.0);
|
||||
apply_color();
|
||||
}
|
||||
|
||||
void ColorPickerShapeRectangle::_sv_square_draw() {
|
||||
draw_sv_square(sv_square, Rect2(Vector2(), sv_square->get_size()));
|
||||
}
|
||||
|
||||
void ColorPickerShapeRectangle::_hue_slider_draw() {
|
||||
const Vector2 size = hue_slider->get_size();
|
||||
hue_slider->draw_texture_rect(color_picker->theme_cache.color_hue, Rect2(0, 0, -size.y, size.x), false, Color(1, 1, 1), true);
|
||||
|
||||
draw_focus_rect(hue_slider);
|
||||
|
||||
int y = size.y * color_picker->h;
|
||||
const Color color = Color::from_hsv(color_picker->h, 1, 1);
|
||||
hue_slider->draw_line(Vector2(0, y), Vector2(size.x, y), color.inverted());
|
||||
}
|
||||
|
||||
void ColorPickerShapeRectangle::_initialize_controls() {
|
||||
sv_square = memnew(Control);
|
||||
color_picker->shape_container->add_child(sv_square);
|
||||
sv_square->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeRectangle::_sv_square_input));
|
||||
sv_square->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeRectangle::_sv_square_draw));
|
||||
connect_shape_focus(sv_square);
|
||||
|
||||
hue_slider = memnew(Control);
|
||||
color_picker->shape_container->add_child(hue_slider);
|
||||
hue_slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeRectangle::_hue_slider_input));
|
||||
hue_slider->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeRectangle::_hue_slider_draw));
|
||||
connect_shape_focus(hue_slider);
|
||||
|
||||
controls.append(sv_square);
|
||||
controls.append(hue_slider);
|
||||
}
|
||||
|
||||
void ColorPickerShapeRectangle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
|
||||
if (sv_square->has_focus()) {
|
||||
color_picker->s = CLAMP(color_picker->s + p_color_change_vector.x / 100.0, 0, 1);
|
||||
color_picker->v = CLAMP(color_picker->v - p_color_change_vector.y / 100.0, 0, 1);
|
||||
} else if (hue_slider->has_focus()) {
|
||||
color_picker->h = CLAMP(color_picker->h + p_color_change_vector.y * echo_multiplier / 360.0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPickerShapeRectangle::update_theme() {
|
||||
const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
|
||||
sv_square->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
|
||||
hue_slider->set_custom_minimum_size(Size2(theme_cache.h_width, 0));
|
||||
}
|
||||
|
||||
void ColorPickerShapeRectangle::grab_focus() {
|
||||
hue_slider->grab_focus();
|
||||
}
|
||||
|
||||
float ColorPickerShapeWheel::_get_h_on_wheel(const Vector2 &p_color_change_vector) {
|
||||
int h_change = get_edge_h_change(p_color_change_vector);
|
||||
|
||||
float target_h = Math::wrapf(color_picker->h + h_change / 360.0, 0, 1);
|
||||
int current_quarter = color_picker->h * 4;
|
||||
int future_quarter = target_h * 4;
|
||||
|
||||
if (p_color_change_vector.y > 0 && ((future_quarter == 0 && current_quarter == 1) || (future_quarter == 1 && current_quarter == 0))) {
|
||||
rotate_next_echo_event = !rotate_next_echo_event;
|
||||
} else if (p_color_change_vector.y < 0 && ((future_quarter == 2 && current_quarter == 3) || (future_quarter == 3 && current_quarter == 2))) {
|
||||
rotate_next_echo_event = !rotate_next_echo_event;
|
||||
} else if (p_color_change_vector.x < 0 && ((future_quarter == 1 && current_quarter == 2) || (future_quarter == 2 && current_quarter == 1))) {
|
||||
rotate_next_echo_event = !rotate_next_echo_event;
|
||||
} else if (p_color_change_vector.x > 0 && ((future_quarter == 3 && current_quarter == 0) || (future_quarter == 0 && current_quarter == 3))) {
|
||||
rotate_next_echo_event = !rotate_next_echo_event;
|
||||
}
|
||||
|
||||
return target_h;
|
||||
}
|
||||
|
||||
void ColorPickerShapeWheel::_reset_wheel_focus() {
|
||||
wheel_focused = true;
|
||||
}
|
||||
|
||||
void ColorPickerShapeWheel::_wheel_input(const Ref<InputEvent> &p_event) {
|
||||
handle_cursor_editing(p_event, wheel_uv);
|
||||
if (!cursor_editing) {
|
||||
// Wheel and inner square are the same control, so focus has to be moved manually.
|
||||
if (!wheel_focused && p_event->is_action_pressed("ui_down", true)) {
|
||||
wheel_focused = true;
|
||||
wheel_uv->queue_redraw();
|
||||
color_picker->accept_event();
|
||||
return;
|
||||
} else if (wheel_focused && p_event->is_action_pressed("ui_up", true)) {
|
||||
wheel_focused = false;
|
||||
wheel_uv->queue_redraw();
|
||||
color_picker->accept_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 event_position;
|
||||
bool is_click = false;
|
||||
if (!can_handle(p_event, event_position, &is_click)) {
|
||||
if (is_click) {
|
||||
// Released mouse button while dragging wheel.
|
||||
spinning = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const Vector2 uv_size = wheel_uv->get_size();
|
||||
const Vector2 ring_radius = uv_size * Math::SQRT12 * WHEEL_RADIUS;
|
||||
const Vector2 center = uv_size * 0.5;
|
||||
|
||||
if (is_click && !spinning) {
|
||||
real_t dist = center.distance_to(event_position);
|
||||
if (dist >= center.x * WHEEL_RADIUS * 2.0 && dist <= center.x) {
|
||||
spinning = true;
|
||||
if (!wheel_focused) {
|
||||
cursor_editing = true;
|
||||
wheel_focused = true;
|
||||
}
|
||||
} else if (dist > center.x) {
|
||||
// Clicked outside the wheel.
|
||||
cancel_event();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if (spinning) {
|
||||
real_t rad = center.angle_to_point(event_position);
|
||||
color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
|
||||
apply_color();
|
||||
return;
|
||||
}
|
||||
|
||||
const Rect2 uv_rect(center - ring_radius, ring_radius * 2.0);
|
||||
event_position -= uv_rect.position;
|
||||
event_position /= uv_rect.size;
|
||||
|
||||
if (is_click && (event_position.x < 0 || event_position.x > 1 || event_position.y < 0 || event_position.y > 1)) {
|
||||
// Clicked inside the wheel, but outside the square.
|
||||
cancel_event();
|
||||
return;
|
||||
}
|
||||
|
||||
event_position = event_position.clampf(0.0, 1.0);
|
||||
|
||||
color_picker->s = event_position.x;
|
||||
color_picker->v = 1.0 - event_position.y;
|
||||
if (wheel_focused) {
|
||||
cursor_editing = true;
|
||||
wheel_focused = false;
|
||||
}
|
||||
|
||||
apply_color();
|
||||
}
|
||||
|
||||
void ColorPickerShapeWheel::_wheel_draw() {
|
||||
wheel->draw_rect(Rect2(Point2(), wheel->get_size()), Color(1, 1, 1));
|
||||
}
|
||||
|
||||
void ColorPickerShapeWheel::_wheel_uv_draw() {
|
||||
const Vector2 uv_size = wheel_uv->get_size();
|
||||
const Vector2 ring_radius = uv_size * Math::SQRT12 * WHEEL_RADIUS;
|
||||
const Vector2 center = uv_size * 0.5;
|
||||
|
||||
const Rect2 uv_rect(center - ring_radius, ring_radius * 2.0);
|
||||
draw_sv_square(wheel_uv, uv_rect, !wheel_focused);
|
||||
if (wheel_focused) {
|
||||
draw_focus_circle(wheel_uv);
|
||||
}
|
||||
|
||||
float radius = WHEEL_RADIUS * 2.0;
|
||||
radius += (1.0 - radius) * 0.5;
|
||||
const Vector2 cursor_pos = center +
|
||||
Vector2(center.x * Math::cos(color_picker->h * Math::TAU) * radius,
|
||||
center.y * Math::sin(color_picker->h * Math::TAU) * radius);
|
||||
draw_cursor(wheel_uv, cursor_pos, false);
|
||||
}
|
||||
|
||||
void ColorPickerShapeWheel::_initialize_controls() {
|
||||
wheel_margin = memnew(MarginContainer);
|
||||
color_picker->shape_container->add_child(wheel_margin);
|
||||
|
||||
Ref<ShaderMaterial> material;
|
||||
material.instantiate();
|
||||
material->set_shader(ColorPicker::wheel_shader);
|
||||
material->set_shader_parameter("wheel_radius", WHEEL_RADIUS);
|
||||
|
||||
wheel = memnew(Control);
|
||||
wheel->set_material(material);
|
||||
wheel_margin->add_child(wheel);
|
||||
wheel->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeWheel::_wheel_draw));
|
||||
|
||||
wheel_uv = memnew(Control);
|
||||
wheel_margin->add_child(wheel_uv);
|
||||
wheel_uv->connect(SceneStringName(focus_entered), callable_mp(this, &ColorPickerShapeWheel::_reset_wheel_focus));
|
||||
wheel_uv->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeWheel::_wheel_input));
|
||||
wheel_uv->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeWheel::_wheel_uv_draw));
|
||||
connect_shape_focus(wheel_uv);
|
||||
|
||||
controls.append(wheel_margin);
|
||||
controls.append(wheel);
|
||||
controls.append(wheel_uv);
|
||||
}
|
||||
|
||||
void ColorPickerShapeWheel::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
|
||||
if (wheel_focused) {
|
||||
if (p_is_echo && rotate_next_echo_event) {
|
||||
color_picker->h = _get_h_on_wheel(-p_color_change_vector);
|
||||
} else {
|
||||
rotate_next_echo_event = false;
|
||||
color_picker->h = _get_h_on_wheel(p_color_change_vector);
|
||||
}
|
||||
} else {
|
||||
color_picker->s = CLAMP(color_picker->s + p_color_change_vector.x / 100.0, 0, 1);
|
||||
color_picker->v = CLAMP(color_picker->v - p_color_change_vector.y / 100.0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPickerShapeWheel::update_theme() {
|
||||
const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
|
||||
wheel_margin->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
|
||||
wheel_margin->add_theme_constant_override(SNAME("margin_bottom"), 8 * theme_cache.base_scale);
|
||||
}
|
||||
|
||||
void ColorPickerShapeWheel::grab_focus() {
|
||||
wheel_uv->grab_focus();
|
||||
}
|
||||
|
||||
void ColorPickerShapeCircle::update_circle_cursor(const Vector2 &p_color_change_vector, const Vector2 &p_center, const Vector2 &p_hue_offset) {
|
||||
if (circle_keyboard_joypad_picker_cursor_position == Vector2i()) {
|
||||
circle_keyboard_joypad_picker_cursor_position = p_center + p_hue_offset;
|
||||
}
|
||||
|
||||
Vector2i potential_cursor_position = circle_keyboard_joypad_picker_cursor_position + p_color_change_vector;
|
||||
real_t potential_new_cursor_distance = p_center.distance_to(potential_cursor_position);
|
||||
real_t dist_pre = p_center.distance_to(circle_keyboard_joypad_picker_cursor_position);
|
||||
if (color_picker->s < 1 || potential_new_cursor_distance < dist_pre) {
|
||||
circle_keyboard_joypad_picker_cursor_position += p_color_change_vector;
|
||||
real_t dist = p_center.distance_to(circle_keyboard_joypad_picker_cursor_position);
|
||||
real_t rad = p_center.angle_to_point(circle_keyboard_joypad_picker_cursor_position);
|
||||
color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
|
||||
color_picker->s = CLAMP(dist / p_center.x, 0, 1);
|
||||
} else {
|
||||
color_picker->h = get_h_on_circle_edge(p_color_change_vector);
|
||||
circle_keyboard_joypad_picker_cursor_position = Vector2i();
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPickerShapeCircle::_initialize_controls() {
|
||||
circle_margin = memnew(MarginContainer);
|
||||
color_picker->shape_container->add_child(circle_margin);
|
||||
|
||||
Ref<ShaderMaterial> material;
|
||||
material.instantiate();
|
||||
material->set_shader(_get_shader());
|
||||
|
||||
circle = memnew(Control);
|
||||
circle->set_material(material);
|
||||
circle_margin->add_child(circle);
|
||||
circle->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeCircle::_circle_draw));
|
||||
|
||||
circle_overlay = memnew(Control);
|
||||
circle_overlay->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
|
||||
circle->add_child(circle_overlay);
|
||||
circle_overlay->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeCircle::_circle_input));
|
||||
circle_overlay->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeCircle::_circle_overlay_draw));
|
||||
connect_shape_focus(circle_overlay);
|
||||
|
||||
value_slider = memnew(Control);
|
||||
color_picker->shape_container->add_child(value_slider);
|
||||
value_slider->connect(SceneStringName(gui_input), callable_mp(this, &ColorPickerShapeCircle::_value_slider_input));
|
||||
value_slider->connect(SceneStringName(draw), callable_mp(this, &ColorPickerShapeCircle::_value_slider_draw));
|
||||
connect_shape_focus(value_slider);
|
||||
|
||||
controls.append(circle_margin);
|
||||
controls.append(circle);
|
||||
controls.append(circle_overlay);
|
||||
controls.append(value_slider);
|
||||
}
|
||||
|
||||
void ColorPickerShapeCircle::update_theme() {
|
||||
const ColorPicker::ThemeCache &theme_cache = color_picker->theme_cache;
|
||||
circle_margin->set_custom_minimum_size(Size2(theme_cache.sv_width, theme_cache.sv_height));
|
||||
circle_margin->add_theme_constant_override(SNAME("margin_bottom"), 8 * theme_cache.base_scale);
|
||||
value_slider->set_custom_minimum_size(Size2(theme_cache.h_width, 0));
|
||||
}
|
||||
|
||||
void ColorPickerShapeCircle::grab_focus() {
|
||||
circle_overlay->grab_focus();
|
||||
}
|
||||
|
||||
void ColorPickerShapeVHSCircle::_circle_input(const Ref<InputEvent> &p_event) {
|
||||
handle_cursor_editing(p_event, circle_overlay);
|
||||
|
||||
Vector2 event_position;
|
||||
bool is_click = false;
|
||||
if (!can_handle(p_event, event_position, &is_click)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 center = circle->get_size() * 0.5;
|
||||
real_t dist = center.distance_to(event_position);
|
||||
if (is_click && dist > center.x) {
|
||||
// Clicked outside the circle.
|
||||
cancel_event();
|
||||
return;
|
||||
}
|
||||
|
||||
real_t rad = center.angle_to_point(event_position);
|
||||
color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
|
||||
color_picker->s = CLAMP(dist / center.x, 0, 1);
|
||||
color_picker->ok_hsl_h = color_picker->h;
|
||||
color_picker->ok_hsl_s = color_picker->s;
|
||||
circle_keyboard_joypad_picker_cursor_position = Vector2i();
|
||||
|
||||
apply_color();
|
||||
}
|
||||
|
||||
void ColorPickerShapeVHSCircle::_value_slider_input(const Ref<InputEvent> &p_event) {
|
||||
handle_cursor_editing(p_event, value_slider);
|
||||
|
||||
Vector2 event_position;
|
||||
if (!can_handle(p_event, event_position)) {
|
||||
return;
|
||||
}
|
||||
color_picker->v = 1.0 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
|
||||
apply_color();
|
||||
}
|
||||
|
||||
void ColorPickerShapeVHSCircle::_circle_draw() {
|
||||
Ref<ShaderMaterial> material = circle->get_material();
|
||||
material->set_shader_parameter(SNAME("v"), color_picker->v);
|
||||
circle->draw_rect(Rect2(Point2(), circle->get_size()), Color(1, 1, 1));
|
||||
}
|
||||
|
||||
void ColorPickerShapeVHSCircle::_circle_overlay_draw() {
|
||||
draw_focus_circle(circle_overlay);
|
||||
draw_circle_cursor(circle_overlay, color_picker->h);
|
||||
}
|
||||
|
||||
void ColorPickerShapeVHSCircle::_value_slider_draw() {
|
||||
const Vector2 size = value_slider->get_size();
|
||||
PackedVector2Array points{
|
||||
Vector2(),
|
||||
Vector2(size.x, 0),
|
||||
size,
|
||||
Vector2(0, size.y)
|
||||
};
|
||||
|
||||
Color color = Color::from_hsv(color_picker->h, color_picker->s, 1);
|
||||
PackedColorArray colors = {
|
||||
color,
|
||||
color,
|
||||
Color(),
|
||||
Color()
|
||||
};
|
||||
|
||||
value_slider->draw_polygon(points, colors);
|
||||
|
||||
draw_focus_rect(value_slider);
|
||||
|
||||
int y = size.y * (1 - CLAMP(color_picker->v, 0, 1));
|
||||
color.set_hsv(color_picker->h, 1, color_picker->v);
|
||||
value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), color.inverted());
|
||||
}
|
||||
|
||||
void ColorPickerShapeVHSCircle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
|
||||
if (circle_overlay->has_focus()) {
|
||||
const Vector2 center = circle_overlay->get_size() / 2.0;
|
||||
const Vector2 hue_offset = center * Vector2(Math::cos(color_picker->h * Math::TAU), Math::sin(color_picker->h * Math::TAU)) * color_picker->s;
|
||||
update_circle_cursor(p_color_change_vector, center, hue_offset);
|
||||
} else if (value_slider->has_focus()) {
|
||||
color_picker->v = CLAMP(color_picker->v - p_color_change_vector.y * echo_multiplier / 100.0, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPickerShapeOKHSLCircle::_circle_input(const Ref<InputEvent> &p_event) {
|
||||
handle_cursor_editing(p_event, circle_overlay);
|
||||
|
||||
Vector2 event_position;
|
||||
bool is_click = false;
|
||||
if (!can_handle(p_event, event_position, &is_click)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Vector2 center = circle->get_size() * 0.5;
|
||||
real_t dist = center.distance_to(event_position);
|
||||
if (is_click && dist > center.x) {
|
||||
// Clicked outside the circle.
|
||||
cancel_event();
|
||||
return;
|
||||
}
|
||||
|
||||
real_t rad = center.angle_to_point(event_position);
|
||||
color_picker->h = ((rad >= 0) ? rad : (Math::TAU + rad)) / Math::TAU;
|
||||
color_picker->s = CLAMP(dist / center.x, 0, 1);
|
||||
color_picker->ok_hsl_h = color_picker->h;
|
||||
color_picker->ok_hsl_s = color_picker->s;
|
||||
circle_keyboard_joypad_picker_cursor_position = Vector2i();
|
||||
|
||||
apply_color();
|
||||
}
|
||||
|
||||
void ColorPickerShapeOKHSLCircle::_value_slider_input(const Ref<InputEvent> &p_event) {
|
||||
handle_cursor_editing(p_event, value_slider);
|
||||
|
||||
Vector2 event_position;
|
||||
if (!can_handle(p_event, event_position)) {
|
||||
return;
|
||||
}
|
||||
color_picker->ok_hsl_l = 1.0 - CLAMP(event_position.y / value_slider->get_size().y, 0.0, 1.0);
|
||||
apply_color();
|
||||
}
|
||||
|
||||
void ColorPickerShapeOKHSLCircle::_circle_draw() {
|
||||
Ref<ShaderMaterial> material = circle->get_material();
|
||||
material->set_shader_parameter(SNAME("ok_hsl_l"), color_picker->ok_hsl_l);
|
||||
circle->draw_rect(Rect2(Point2(), circle->get_size()), Color(1, 1, 1));
|
||||
}
|
||||
|
||||
void ColorPickerShapeOKHSLCircle::_circle_overlay_draw() {
|
||||
draw_focus_circle(circle_overlay);
|
||||
draw_circle_cursor(circle_overlay, color_picker->ok_hsl_h);
|
||||
}
|
||||
|
||||
void ColorPickerShapeOKHSLCircle::_value_slider_draw() {
|
||||
const float ok_hsl_h = color_picker->ok_hsl_h;
|
||||
const float ok_hsl_s = color_picker->ok_hsl_s;
|
||||
const float ok_hsl_l = color_picker->ok_hsl_l;
|
||||
|
||||
const Vector2 size = value_slider->get_size();
|
||||
PackedVector2Array points{
|
||||
Vector2(size.x, 0),
|
||||
Vector2(size.x, size.y * 0.5),
|
||||
size,
|
||||
Vector2(0, size.y),
|
||||
Vector2(0, size.y * 0.5),
|
||||
Vector2()
|
||||
};
|
||||
|
||||
Color color1 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 1);
|
||||
Color color2 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0.5);
|
||||
Color color3 = Color::from_ok_hsl(ok_hsl_h, ok_hsl_s, 0);
|
||||
PackedColorArray colors = {
|
||||
color1,
|
||||
color2,
|
||||
color3,
|
||||
color3,
|
||||
color2,
|
||||
color1,
|
||||
};
|
||||
value_slider->draw_polygon(points, colors);
|
||||
|
||||
draw_focus_rect(value_slider);
|
||||
|
||||
int y = size.y * (1 - CLAMP(ok_hsl_l, 0, 1));
|
||||
value_slider->draw_line(Vector2(0, y), Vector2(size.x, y), Color::from_hsv(ok_hsl_h, 1, ok_hsl_l).inverted());
|
||||
}
|
||||
|
||||
void ColorPickerShapeOKHSLCircle::_update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) {
|
||||
if (circle_overlay->has_focus()) {
|
||||
const Vector2 center = circle_overlay->get_size() / 2.0;
|
||||
const Vector2 hue_offset = center * Vector2(Math::cos(color_picker->ok_hsl_h * Math::TAU), Math::sin(color_picker->ok_hsl_h * Math::TAU)) * color_picker->ok_hsl_s;
|
||||
update_circle_cursor(p_color_change_vector, center, hue_offset);
|
||||
color_picker->ok_hsl_h = color_picker->h;
|
||||
color_picker->ok_hsl_s = color_picker->s;
|
||||
} else if (value_slider->has_focus()) {
|
||||
color_picker->ok_hsl_l = CLAMP(color_picker->ok_hsl_l - p_color_change_vector.y * echo_multiplier / 100.0, 0, 1);
|
||||
}
|
||||
}
|
||||
215
engine/scene/gui/color_picker_shape.h
Normal file
215
engine/scene/gui/color_picker_shape.h
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
/**************************************************************************/
|
||||
/* color_picker_shape.h */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/color_picker.h"
|
||||
|
||||
class ColorPickerShape : public Object {
|
||||
GDCLASS(ColorPickerShape, Object);
|
||||
|
||||
void _emit_color_changed();
|
||||
|
||||
protected:
|
||||
ColorPicker *color_picker = nullptr;
|
||||
bool is_dragging = false;
|
||||
|
||||
virtual void _initialize_controls() = 0;
|
||||
virtual void _update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) = 0;
|
||||
|
||||
bool can_handle(const Ref<InputEvent> &p_event, Vector2 &r_position, bool *r_is_click = nullptr);
|
||||
void apply_color();
|
||||
void cancel_event();
|
||||
|
||||
void draw_focus_rect(Control *p_control, const Rect2 &p_rect = Rect2());
|
||||
void draw_focus_circle(Control *p_control);
|
||||
void draw_sv_square(Control *p_control, const Rect2 &p_square, bool p_draw_focus = true);
|
||||
void draw_cursor(Control *p_control, const Vector2 &p_center, bool p_draw_bg = true);
|
||||
void draw_circle_cursor(Control *p_control, float p_hue);
|
||||
|
||||
void connect_shape_focus(Control *p_shape);
|
||||
void shape_focus_entered();
|
||||
void shape_focus_exited();
|
||||
|
||||
void handle_cursor_editing(const Ref<InputEvent> &p_event, Control *p_control);
|
||||
int get_edge_h_change(const Vector2 &p_color_change_vector);
|
||||
float get_h_on_circle_edge(const Vector2 &p_color_change_vector);
|
||||
|
||||
public:
|
||||
Vector<Control *> controls;
|
||||
bool is_initialized = false;
|
||||
bool cursor_editing = false;
|
||||
float echo_multiplier = 1;
|
||||
|
||||
virtual String get_name() const = 0;
|
||||
virtual Ref<Texture2D> get_icon() const = 0;
|
||||
virtual bool is_ok_hsl() const { return false; }
|
||||
|
||||
void initialize_controls();
|
||||
virtual void update_theme() = 0;
|
||||
void update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo);
|
||||
virtual void grab_focus() = 0;
|
||||
|
||||
ColorPickerShape(ColorPicker *p_color_picker);
|
||||
};
|
||||
|
||||
class ColorPickerShapeRectangle : public ColorPickerShape {
|
||||
Control *sv_square = nullptr;
|
||||
Control *hue_slider = nullptr;
|
||||
|
||||
void _sv_square_input(const Ref<InputEvent> &p_event);
|
||||
void _hue_slider_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
void _sv_square_draw();
|
||||
void _hue_slider_draw();
|
||||
|
||||
protected:
|
||||
virtual void _initialize_controls() override;
|
||||
virtual void _update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) override;
|
||||
|
||||
public:
|
||||
virtual String get_name() const override { return ETR("HSV Rectangle"); }
|
||||
virtual Ref<Texture2D> get_icon() const override { return color_picker->theme_cache.shape_rect; }
|
||||
virtual void update_theme() override;
|
||||
virtual void grab_focus() override;
|
||||
|
||||
ColorPickerShapeRectangle(ColorPicker *p_color_picker) :
|
||||
ColorPickerShape(p_color_picker) {}
|
||||
};
|
||||
|
||||
class ColorPickerShapeWheel : public ColorPickerShape {
|
||||
GDCLASS(ColorPickerShapeWheel, ColorPickerShape);
|
||||
|
||||
inline static constexpr float WHEEL_RADIUS = 0.42;
|
||||
|
||||
MarginContainer *wheel_margin = nullptr;
|
||||
Control *wheel = nullptr;
|
||||
Control *wheel_uv = nullptr;
|
||||
|
||||
bool wheel_focused = true;
|
||||
bool spinning = false;
|
||||
bool rotate_next_echo_event = false;
|
||||
|
||||
float _get_h_on_wheel(const Vector2 &p_color_change_vector);
|
||||
void _reset_wheel_focus();
|
||||
|
||||
void _wheel_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
void _wheel_draw();
|
||||
void _wheel_uv_draw();
|
||||
|
||||
protected:
|
||||
virtual void _initialize_controls() override;
|
||||
virtual void _update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) override;
|
||||
|
||||
public:
|
||||
virtual String get_name() const override { return ETR("HSV Wheel"); }
|
||||
virtual Ref<Texture2D> get_icon() const override { return color_picker->theme_cache.shape_rect_wheel; }
|
||||
virtual void update_theme() override;
|
||||
virtual void grab_focus() override;
|
||||
|
||||
ColorPickerShapeWheel(ColorPicker *p_color_picker) :
|
||||
ColorPickerShape(p_color_picker) {}
|
||||
};
|
||||
|
||||
class ColorPickerShapeCircle : public ColorPickerShape {
|
||||
GDCLASS(ColorPickerShapeCircle, ColorPickerShape);
|
||||
|
||||
protected:
|
||||
MarginContainer *circle_margin = nullptr;
|
||||
Control *circle = nullptr;
|
||||
Control *circle_overlay = nullptr;
|
||||
Control *value_slider = nullptr;
|
||||
|
||||
Vector2i circle_keyboard_joypad_picker_cursor_position;
|
||||
|
||||
virtual void _circle_input(const Ref<InputEvent> &p_event) = 0;
|
||||
virtual void _value_slider_input(const Ref<InputEvent> &p_event) = 0;
|
||||
|
||||
virtual void _circle_draw() = 0;
|
||||
virtual void _circle_overlay_draw() = 0;
|
||||
virtual void _value_slider_draw() = 0;
|
||||
|
||||
void update_circle_cursor(const Vector2 &p_color_change_vector, const Vector2 &p_center, const Vector2 &p_hue_offset);
|
||||
|
||||
public:
|
||||
virtual Ref<Shader> _get_shader() const = 0;
|
||||
virtual void _initialize_controls() override;
|
||||
|
||||
virtual Ref<Texture2D> get_icon() const override { return color_picker->theme_cache.shape_circle; }
|
||||
virtual void update_theme() override;
|
||||
virtual void grab_focus() override;
|
||||
|
||||
ColorPickerShapeCircle(ColorPicker *p_color_picker) :
|
||||
ColorPickerShape(p_color_picker) {}
|
||||
};
|
||||
|
||||
class ColorPickerShapeVHSCircle : public ColorPickerShapeCircle {
|
||||
GDCLASS(ColorPickerShapeVHSCircle, ColorPickerShapeCircle);
|
||||
|
||||
protected:
|
||||
virtual void _update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) override;
|
||||
virtual Ref<Shader> _get_shader() const override { return ColorPicker::circle_shader; }
|
||||
|
||||
virtual void _circle_input(const Ref<InputEvent> &p_event) override;
|
||||
virtual void _value_slider_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
virtual void _circle_draw() override;
|
||||
virtual void _circle_overlay_draw() override;
|
||||
virtual void _value_slider_draw() override;
|
||||
|
||||
public:
|
||||
virtual String get_name() const override { return ETR("VHS Circle"); }
|
||||
|
||||
ColorPickerShapeVHSCircle(ColorPicker *p_color_picker) :
|
||||
ColorPickerShapeCircle(p_color_picker) {}
|
||||
};
|
||||
|
||||
class ColorPickerShapeOKHSLCircle : public ColorPickerShapeCircle {
|
||||
GDCLASS(ColorPickerShapeOKHSLCircle, ColorPickerShapeCircle);
|
||||
|
||||
protected:
|
||||
virtual void _update_cursor(const Vector2 &p_color_change_vector, bool p_is_echo) override;
|
||||
virtual Ref<Shader> _get_shader() const override { return ColorPicker::circle_ok_color_shader; }
|
||||
|
||||
virtual void _circle_input(const Ref<InputEvent> &p_event) override;
|
||||
virtual void _value_slider_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
virtual void _circle_draw() override;
|
||||
virtual void _circle_overlay_draw() override;
|
||||
virtual void _value_slider_draw() override;
|
||||
|
||||
public:
|
||||
virtual String get_name() const override { return ETR("OKHSL Circle"); }
|
||||
virtual bool is_ok_hsl() const override { return true; }
|
||||
|
||||
ColorPickerShapeOKHSLCircle(ColorPicker *p_color_picker) :
|
||||
ColorPickerShapeCircle(p_color_picker) {}
|
||||
};
|
||||
|
|
@ -35,6 +35,7 @@ void ColorRect::set_color(const Color &p_color) {
|
|||
return;
|
||||
}
|
||||
color = p_color;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +45,13 @@ Color ColorRect::get_color() const {
|
|||
|
||||
void ColorRect::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_color_value(ae, color);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
draw_rect(Rect2(Point2(), get_size()), color);
|
||||
} break;
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef COLOR_RECT_H
|
||||
#define COLOR_RECT_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
|
|
@ -46,5 +45,3 @@ public:
|
|||
void set_color(const Color &p_color);
|
||||
Color get_color() const;
|
||||
};
|
||||
|
||||
#endif // COLOR_RECT_H
|
||||
|
|
|
|||
|
|
@ -184,6 +184,13 @@ Vector<int> Container::get_allowed_size_flags_vertical() const {
|
|||
|
||||
void Container::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_CONTAINER);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_RESIZED:
|
||||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CONTAINER_H
|
||||
#define CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
|
|
@ -75,5 +74,3 @@ public:
|
|||
|
||||
Container();
|
||||
};
|
||||
|
||||
#endif // CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -32,8 +32,12 @@
|
|||
|
||||
#include "container.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/input/input_map.h"
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/translation_server.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/gui/panel.h"
|
||||
#include "scene/gui/scroll_container.h"
|
||||
#include "scene/main/canvas_layer.h"
|
||||
#include "scene/main/window.h"
|
||||
|
|
@ -56,18 +60,10 @@ Dictionary Control::_edit_get_state() const {
|
|||
s["scale"] = get_scale();
|
||||
s["pivot"] = get_pivot_offset();
|
||||
|
||||
Array anchors;
|
||||
anchors.push_back(get_anchor(SIDE_LEFT));
|
||||
anchors.push_back(get_anchor(SIDE_TOP));
|
||||
anchors.push_back(get_anchor(SIDE_RIGHT));
|
||||
anchors.push_back(get_anchor(SIDE_BOTTOM));
|
||||
Array anchors = { get_anchor(SIDE_LEFT), get_anchor(SIDE_TOP), get_anchor(SIDE_RIGHT), get_anchor(SIDE_BOTTOM) };
|
||||
s["anchors"] = anchors;
|
||||
|
||||
Array offsets;
|
||||
offsets.push_back(get_offset(SIDE_LEFT));
|
||||
offsets.push_back(get_offset(SIDE_TOP));
|
||||
offsets.push_back(get_offset(SIDE_RIGHT));
|
||||
offsets.push_back(get_offset(SIDE_BOTTOM));
|
||||
Array offsets = { get_offset(SIDE_LEFT), get_offset(SIDE_TOP), get_offset(SIDE_RIGHT), get_offset(SIDE_BOTTOM) };
|
||||
s["offsets"] = offsets;
|
||||
|
||||
s["layout_mode"] = _get_layout_mode();
|
||||
|
|
@ -254,6 +250,27 @@ PackedStringArray Control::get_configuration_warnings() const {
|
|||
return warnings;
|
||||
}
|
||||
|
||||
PackedStringArray Control::get_accessibility_configuration_warnings() const {
|
||||
ERR_READ_THREAD_GUARD_V(PackedStringArray());
|
||||
PackedStringArray warnings = Node::get_accessibility_configuration_warnings();
|
||||
|
||||
String ac_name = get_accessibility_name().strip_edges();
|
||||
if (ac_name.is_empty()) {
|
||||
warnings.push_back(RTR("Accessibility Name must not be empty, or contain only spaces."));
|
||||
}
|
||||
if (ac_name.contains(get_class_name())) {
|
||||
warnings.push_back(RTR("Accessibility Name must not include Node class name."));
|
||||
}
|
||||
for (int i = 0; i < ac_name.length(); i++) {
|
||||
if (is_control(ac_name[i])) {
|
||||
warnings.push_back(RTR("Accessibility Name must not include control character."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
bool Control::is_text_field() const {
|
||||
ERR_READ_THREAD_GUARD_V(false);
|
||||
return false;
|
||||
|
|
@ -481,9 +498,9 @@ void Control::_validate_property(PropertyInfo &p_property) const {
|
|||
}
|
||||
|
||||
// Validate which positioning properties should be displayed depending on the parent and the layout mode.
|
||||
Node *parent_node = get_parent_control();
|
||||
if (!parent_node) {
|
||||
// If there is no parent, display both anchor and container options.
|
||||
Control *parent_control = get_parent_control();
|
||||
if (!parent_control) {
|
||||
// If there is no parent control, display both anchor and container options.
|
||||
|
||||
// Set the layout mode to be disabled with the proper value.
|
||||
if (p_property.name == "layout_mode") {
|
||||
|
|
@ -496,7 +513,7 @@ void Control::_validate_property(PropertyInfo &p_property) const {
|
|||
if (!use_custom_anchors && (p_property.name.begins_with("anchor_") || p_property.name.begins_with("offset_") || p_property.name.begins_with("grow_"))) {
|
||||
p_property.usage ^= PROPERTY_USAGE_EDITOR;
|
||||
}
|
||||
} else if (Object::cast_to<Container>(parent_node)) {
|
||||
} else if (Object::cast_to<Container>(parent_control)) {
|
||||
// If the parent is a container, display only container-related properties.
|
||||
if (p_property.name.begins_with("anchor_") || p_property.name.begins_with("offset_") || p_property.name.begins_with("grow_") || p_property.name == "anchors_preset") {
|
||||
p_property.usage ^= PROPERTY_USAGE_DEFAULT;
|
||||
|
|
@ -508,7 +525,7 @@ void Control::_validate_property(PropertyInfo &p_property) const {
|
|||
p_property.usage |= PROPERTY_USAGE_READ_ONLY;
|
||||
} else if (p_property.name == "size_flags_horizontal" || p_property.name == "size_flags_vertical") {
|
||||
// Filter allowed size flags based on the parent container configuration.
|
||||
Container *parent_container = Object::cast_to<Container>(parent_node);
|
||||
Container *parent_container = Object::cast_to<Container>(parent_control);
|
||||
Vector<int> size_flags;
|
||||
if (p_property.name == "size_flags_horizontal") {
|
||||
size_flags = parent_container->get_allowed_size_flags_horizontal();
|
||||
|
|
@ -548,7 +565,7 @@ void Control::_validate_property(PropertyInfo &p_property) const {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// If the parent is NOT a container or not a control at all, display only anchoring-related properties.
|
||||
// If the parent is a non-container control, display only anchoring-related properties.
|
||||
if (p_property.name.begins_with("size_flags_")) {
|
||||
p_property.usage ^= PROPERTY_USAGE_EDITOR;
|
||||
|
||||
|
|
@ -570,7 +587,7 @@ void Control::_validate_property(PropertyInfo &p_property) const {
|
|||
}
|
||||
|
||||
// Disable the property if it's managed by the parent container.
|
||||
if (!Object::cast_to<Container>(parent_node)) {
|
||||
if (!Object::cast_to<Container>(parent_control)) {
|
||||
return;
|
||||
}
|
||||
bool property_is_managed_by_container = false;
|
||||
|
|
@ -607,11 +624,6 @@ bool Control::_property_get_revert(const StringName &p_name, Variant &r_property
|
|||
|
||||
// Global relations.
|
||||
|
||||
bool Control::is_top_level_control() const {
|
||||
ERR_READ_THREAD_GUARD_V(false);
|
||||
return is_inside_tree() && (!data.parent_canvas_item && !data.RI && is_set_as_top_level());
|
||||
}
|
||||
|
||||
Control *Control::get_parent_control() const {
|
||||
ERR_READ_THREAD_GUARD_V(nullptr);
|
||||
return data.parent_control;
|
||||
|
|
@ -631,10 +643,6 @@ Control *Control::get_root_parent_control() const {
|
|||
const Control *c = Object::cast_to<Control>(ci);
|
||||
if (c) {
|
||||
root = c;
|
||||
|
||||
if (c->data.RI || c->is_top_level_control()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ci = ci->get_parent_item();
|
||||
|
|
@ -875,6 +883,13 @@ void Control::_compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (
|
|||
r_offsets[3] = p_rect.position.y + p_rect.size.y - (p_anchors[3] * parent_rect_size.y);
|
||||
}
|
||||
|
||||
void Control::_compute_edge_positions(Rect2 p_rect, real_t (&r_edge_positions)[4]) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
real_t area = p_rect.size[i & 1];
|
||||
r_edge_positions[i] = data.offset[i] + (data.anchor[i] * area);
|
||||
}
|
||||
}
|
||||
|
||||
/// Presets and layout modes.
|
||||
|
||||
void Control::_set_layout_mode(LayoutMode p_mode) {
|
||||
|
|
@ -905,11 +920,11 @@ void Control::_update_layout_mode() {
|
|||
}
|
||||
|
||||
Control::LayoutMode Control::_get_layout_mode() const {
|
||||
Node *parent_node = get_parent_control();
|
||||
Control *parent_control = get_parent_control();
|
||||
// In these modes the property is read-only.
|
||||
if (!parent_node) {
|
||||
if (!parent_control) {
|
||||
return LayoutMode::LAYOUT_MODE_UNCONTROLLED;
|
||||
} else if (Object::cast_to<Container>(parent_node)) {
|
||||
} else if (Object::cast_to<Container>(parent_control)) {
|
||||
return LayoutMode::LAYOUT_MODE_CONTAINER;
|
||||
}
|
||||
|
||||
|
|
@ -918,20 +933,25 @@ Control::LayoutMode Control::_get_layout_mode() const {
|
|||
return LayoutMode::LAYOUT_MODE_ANCHORS;
|
||||
}
|
||||
|
||||
// Otherwise fallback on what's stored.
|
||||
return data.stored_layout_mode;
|
||||
// Only position/anchors modes are valid for non-container control parent.
|
||||
if (data.stored_layout_mode == LayoutMode::LAYOUT_MODE_POSITION || data.stored_layout_mode == LayoutMode::LAYOUT_MODE_ANCHORS) {
|
||||
return data.stored_layout_mode;
|
||||
}
|
||||
|
||||
// Otherwise fallback to position mode.
|
||||
return LayoutMode::LAYOUT_MODE_POSITION;
|
||||
}
|
||||
|
||||
Control::LayoutMode Control::_get_default_layout_mode() const {
|
||||
Node *parent_node = get_parent_control();
|
||||
Control *parent_control = get_parent_control();
|
||||
// In these modes the property is read-only.
|
||||
if (!parent_node) {
|
||||
if (!parent_control) {
|
||||
return LayoutMode::LAYOUT_MODE_UNCONTROLLED;
|
||||
} else if (Object::cast_to<Container>(parent_node)) {
|
||||
} else if (Object::cast_to<Container>(parent_control)) {
|
||||
return LayoutMode::LAYOUT_MODE_CONTAINER;
|
||||
}
|
||||
|
||||
// Otherwise fallback on the position mode.
|
||||
// Otherwise fallback to the position mode.
|
||||
return LayoutMode::LAYOUT_MODE_POSITION;
|
||||
}
|
||||
|
||||
|
|
@ -1400,10 +1420,13 @@ void Control::set_position(const Point2 &p_point, bool p_keep_offsets) {
|
|||
}
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
real_t edge_pos[4];
|
||||
_compute_edge_positions(get_parent_anchorable_rect(), edge_pos);
|
||||
Size2 offset_size(edge_pos[2] - edge_pos[0], edge_pos[3] - edge_pos[1]);
|
||||
if (p_keep_offsets) {
|
||||
_compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
|
||||
_compute_anchors(Rect2(p_point, offset_size), data.offset, data.anchor);
|
||||
} else {
|
||||
_compute_offsets(Rect2(p_point, data.size_cache), data.anchor, data.offset);
|
||||
_compute_offsets(Rect2(p_point, offset_size), data.anchor, data.offset);
|
||||
}
|
||||
_size_changed();
|
||||
}
|
||||
|
|
@ -1537,6 +1560,7 @@ void Control::set_scale(const Vector2 &p_scale) {
|
|||
}
|
||||
queue_redraw();
|
||||
_notify_transform();
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
Vector2 Control::get_scale() const {
|
||||
|
|
@ -1553,6 +1577,7 @@ void Control::set_rotation(real_t p_radians) {
|
|||
data.rotation = p_radians;
|
||||
queue_redraw();
|
||||
_notify_transform();
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void Control::set_rotation_degrees(real_t p_degrees) {
|
||||
|
|
@ -1579,6 +1604,7 @@ void Control::set_pivot_offset(const Vector2 &p_pivot) {
|
|||
data.pivot_offset = p_pivot;
|
||||
queue_redraw();
|
||||
_notify_transform();
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
Vector2 Control::get_pivot_offset() const {
|
||||
|
|
@ -1690,14 +1716,8 @@ Size2 Control::get_combined_minimum_size() const {
|
|||
|
||||
void Control::_size_changed() {
|
||||
Rect2 parent_rect = get_parent_anchorable_rect();
|
||||
|
||||
real_t edge_pos[4];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
real_t area = parent_rect.size[i & 1];
|
||||
edge_pos[i] = data.offset[i] + (data.anchor[i] * area);
|
||||
}
|
||||
|
||||
_compute_edge_positions(parent_rect, edge_pos);
|
||||
Point2 new_pos_cache = Point2(edge_pos[0], edge_pos[1]);
|
||||
Size2 new_size_cache = Point2(edge_pos[2], edge_pos[3]) - new_pos_cache;
|
||||
|
||||
|
|
@ -1727,8 +1747,11 @@ void Control::_size_changed() {
|
|||
new_size_cache.height = minimum_size.height;
|
||||
}
|
||||
|
||||
bool pos_changed = !new_pos_cache.is_equal_approx(data.pos_cache);
|
||||
bool size_changed = !new_size_cache.is_equal_approx(data.size_cache);
|
||||
bool pos_changed = new_pos_cache != data.pos_cache;
|
||||
bool size_changed = new_size_cache != data.size_cache;
|
||||
// Below helps in getting rid of floating point errors for signaling resized.
|
||||
bool approx_pos_changed = !new_pos_cache.is_equal_approx(data.pos_cache);
|
||||
bool approx_size_changed = !new_size_cache.is_equal_approx(data.size_cache);
|
||||
|
||||
if (pos_changed) {
|
||||
data.pos_cache = new_pos_cache;
|
||||
|
|
@ -1738,13 +1761,13 @@ void Control::_size_changed() {
|
|||
}
|
||||
|
||||
if (is_inside_tree()) {
|
||||
if (pos_changed || size_changed) {
|
||||
if (approx_pos_changed || approx_size_changed) {
|
||||
// Ensure global transform is marked as dirty before `NOTIFICATION_RESIZED` / `item_rect_changed` signal
|
||||
// so an up to date global transform could be obtained when handling these.
|
||||
_notify_transform();
|
||||
|
||||
item_rect_changed(size_changed);
|
||||
if (size_changed) {
|
||||
item_rect_changed(approx_size_changed);
|
||||
if (approx_size_changed) {
|
||||
notification(NOTIFICATION_RESIZED);
|
||||
}
|
||||
}
|
||||
|
|
@ -1752,6 +1775,8 @@ void Control::_size_changed() {
|
|||
if (pos_changed && !size_changed) {
|
||||
_update_canvas_item_transform();
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
} else if (pos_changed) {
|
||||
_notify_transform();
|
||||
}
|
||||
|
|
@ -1866,6 +1891,46 @@ Control::MouseFilter Control::get_mouse_filter() const {
|
|||
return data.mouse_filter;
|
||||
}
|
||||
|
||||
Control::MouseFilter Control::get_mouse_filter_with_recursive() const {
|
||||
ERR_READ_THREAD_GUARD_V(MOUSE_FILTER_IGNORE);
|
||||
if (_is_parent_mouse_disabled()) {
|
||||
return MOUSE_FILTER_IGNORE;
|
||||
}
|
||||
return data.mouse_filter;
|
||||
}
|
||||
|
||||
void Control::set_mouse_recursive_behavior(RecursiveBehavior p_recursive_mouse_behavior) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
ERR_FAIL_INDEX((int)p_recursive_mouse_behavior, 4);
|
||||
if (data.mouse_recursive_behavior == p_recursive_mouse_behavior) {
|
||||
return;
|
||||
}
|
||||
_set_mouse_recursive_behavior_ignore_cache(p_recursive_mouse_behavior);
|
||||
}
|
||||
|
||||
void Control::_set_mouse_recursive_behavior_ignore_cache(RecursiveBehavior p_recursive_mouse_behavior) {
|
||||
data.mouse_recursive_behavior = p_recursive_mouse_behavior;
|
||||
if (p_recursive_mouse_behavior == RECURSIVE_BEHAVIOR_INHERITED) {
|
||||
Control *parent = get_parent_control();
|
||||
if (parent) {
|
||||
_propagate_mouse_behavior_recursively(parent->data.parent_mouse_recursive_behavior, false);
|
||||
} else {
|
||||
_propagate_mouse_behavior_recursively(RECURSIVE_BEHAVIOR_ENABLED, false);
|
||||
}
|
||||
} else {
|
||||
_propagate_mouse_behavior_recursively(p_recursive_mouse_behavior, false);
|
||||
}
|
||||
|
||||
if (get_viewport()) {
|
||||
get_viewport()->_gui_update_mouse_over();
|
||||
}
|
||||
}
|
||||
|
||||
Control::RecursiveBehavior Control::get_mouse_recursive_behavior() const {
|
||||
ERR_READ_THREAD_GUARD_V(RECURSIVE_BEHAVIOR_INHERITED);
|
||||
return data.mouse_recursive_behavior;
|
||||
}
|
||||
|
||||
void Control::set_force_pass_scroll_events(bool p_force_pass_scroll_events) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
data.force_pass_scroll_events = p_force_pass_scroll_events;
|
||||
|
|
@ -1981,7 +2046,40 @@ void Control::force_drag(const Variant &p_data, Control *p_control) {
|
|||
ERR_FAIL_COND(!is_inside_tree());
|
||||
ERR_FAIL_COND(p_data.get_type() == Variant::NIL);
|
||||
|
||||
get_viewport()->_gui_force_drag(this, p_data, p_control);
|
||||
Viewport *vp = get_viewport();
|
||||
|
||||
vp->_gui_force_drag_start();
|
||||
vp->_gui_force_drag(this, p_data, p_control);
|
||||
}
|
||||
|
||||
void Control::accessibility_drag() {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
ERR_FAIL_COND(!is_inside_tree());
|
||||
|
||||
Viewport *vp = get_viewport();
|
||||
|
||||
vp->_gui_force_drag_start();
|
||||
Variant dnd_data = get_drag_data(Vector2(Math::INF, Math::INF));
|
||||
if (dnd_data.get_type() != Variant::NIL) {
|
||||
Window *w = Window::get_from_id(get_window()->get_window_id());
|
||||
if (w) {
|
||||
w->accessibility_announcement(vformat(RTR("%s grabbed. Select target and use %s to drop, use %s to cancel."), vp->gui_get_drag_description(), InputMap::get_singleton()->get_action_description("ui_accessibility_drag_and_drop"), InputMap::get_singleton()->get_action_description("ui_cancel")));
|
||||
}
|
||||
vp->_gui_force_drag(this, dnd_data, nullptr);
|
||||
queue_accessibility_update();
|
||||
} else {
|
||||
vp->_gui_force_drag_cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void Control::accessibility_drop() {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
ERR_FAIL_COND(!is_inside_tree());
|
||||
ERR_FAIL_COND(!get_viewport()->gui_is_dragging());
|
||||
|
||||
get_viewport()->gui_perform_drop_at(Vector2(Math::INF, Math::INF), this);
|
||||
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void Control::set_drag_preview(Control *p_control) {
|
||||
|
|
@ -2000,7 +2098,7 @@ bool Control::is_drag_successful() const {
|
|||
|
||||
void Control::set_focus_mode(FocusMode p_focus_mode) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
ERR_FAIL_INDEX((int)p_focus_mode, 3);
|
||||
ERR_FAIL_INDEX((int)p_focus_mode, 4);
|
||||
|
||||
if (is_inside_tree() && p_focus_mode == FOCUS_NONE && data.focus_mode != FOCUS_NONE && has_focus()) {
|
||||
release_focus();
|
||||
|
|
@ -2014,6 +2112,42 @@ Control::FocusMode Control::get_focus_mode() const {
|
|||
return data.focus_mode;
|
||||
}
|
||||
|
||||
Control::FocusMode Control::get_focus_mode_with_recursive() const {
|
||||
ERR_READ_THREAD_GUARD_V(FOCUS_NONE);
|
||||
if (_is_focus_disabled_recursively()) {
|
||||
return FOCUS_NONE;
|
||||
}
|
||||
return data.focus_mode;
|
||||
}
|
||||
|
||||
void Control::set_focus_recursive_behavior(RecursiveBehavior p_recursive_focus_behavior) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
ERR_FAIL_INDEX((int)p_recursive_focus_behavior, 4);
|
||||
if (data.focus_recursive_behavior == p_recursive_focus_behavior) {
|
||||
return;
|
||||
}
|
||||
_set_focus_recursive_behavior_ignore_cache(p_recursive_focus_behavior);
|
||||
}
|
||||
|
||||
void Control::_set_focus_recursive_behavior_ignore_cache(RecursiveBehavior p_recursive_focus_behavior) {
|
||||
data.focus_recursive_behavior = p_recursive_focus_behavior;
|
||||
if (p_recursive_focus_behavior == RECURSIVE_BEHAVIOR_INHERITED) {
|
||||
Control *parent = get_parent_control();
|
||||
if (parent) {
|
||||
_propagate_focus_behavior_recursively(parent->data.parent_focus_recursive_behavior, false);
|
||||
} else {
|
||||
_propagate_focus_behavior_recursively(RECURSIVE_BEHAVIOR_ENABLED, false);
|
||||
}
|
||||
} else {
|
||||
_propagate_focus_behavior_recursively(p_recursive_focus_behavior, false);
|
||||
}
|
||||
}
|
||||
|
||||
Control::RecursiveBehavior Control::get_focus_recursive_behavior() const {
|
||||
ERR_READ_THREAD_GUARD_V(RECURSIVE_BEHAVIOR_INHERITED);
|
||||
return data.focus_recursive_behavior;
|
||||
}
|
||||
|
||||
bool Control::has_focus() const {
|
||||
ERR_READ_THREAD_GUARD_V(false);
|
||||
return is_inside_tree() && get_viewport()->_gui_control_has_focus(this);
|
||||
|
|
@ -2054,7 +2188,7 @@ static Control *_next_control(Control *p_from) {
|
|||
return nullptr; // Can't go above.
|
||||
}
|
||||
|
||||
Control *parent = Object::cast_to<Control>(p_from->get_parent());
|
||||
Control *parent = p_from->get_parent_control();
|
||||
|
||||
if (!parent) {
|
||||
return nullptr;
|
||||
|
|
@ -2077,21 +2211,25 @@ static Control *_next_control(Control *p_from) {
|
|||
|
||||
Control *Control::find_next_valid_focus() const {
|
||||
ERR_READ_THREAD_GUARD_V(nullptr);
|
||||
|
||||
// If the focus property is manually overwritten, attempt to use it.
|
||||
if (!data.focus_next.is_empty()) {
|
||||
Node *n = get_node_or_null(data.focus_next);
|
||||
ERR_FAIL_NULL_V_MSG(n, nullptr, "Next focus node path is invalid: '" + data.focus_next + "'.");
|
||||
Control *c = Object::cast_to<Control>(n);
|
||||
ERR_FAIL_NULL_V_MSG(c, nullptr, "Next focus node is not a control: '" + n->get_name() + "'.");
|
||||
if (c->is_visible_in_tree() && c->get_focus_mode_with_recursive() != FOCUS_NONE) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
Control *from = const_cast<Control *>(this);
|
||||
bool ac_enabled = get_tree() && get_tree()->is_accessibility_enabled();
|
||||
|
||||
// Index of the current `Control` subtree within the containing `Window`.
|
||||
int window_next = -1;
|
||||
|
||||
while (true) {
|
||||
// If the focus property is manually overwritten, attempt to use it.
|
||||
|
||||
if (!data.focus_next.is_empty()) {
|
||||
Node *n = get_node_or_null(data.focus_next);
|
||||
ERR_FAIL_NULL_V_MSG(n, nullptr, "Next focus node path is invalid: '" + data.focus_next + "'.");
|
||||
Control *c = Object::cast_to<Control>(n);
|
||||
ERR_FAIL_NULL_V_MSG(c, nullptr, "Next focus node is not a control: '" + n->get_name() + "'.");
|
||||
if (c->is_visible() && c->get_focus_mode() != FOCUS_NONE) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
// Find next child.
|
||||
|
||||
Control *next_child = nullptr;
|
||||
|
|
@ -2110,83 +2248,121 @@ Control *Control::find_next_valid_focus() const {
|
|||
next_child = _next_control(from);
|
||||
if (!next_child) { // Nothing else. Go up and find either window or subwindow.
|
||||
next_child = const_cast<Control *>(this);
|
||||
while (next_child && !next_child->is_set_as_top_level()) {
|
||||
next_child = cast_to<Control>(next_child->get_parent());
|
||||
|
||||
while (next_child) {
|
||||
if (next_child->is_set_as_top_level()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (next_child->data.RI) {
|
||||
break;
|
||||
}
|
||||
next_child = next_child->data.parent_control;
|
||||
}
|
||||
|
||||
if (!next_child) {
|
||||
next_child = const_cast<Control *>(this);
|
||||
while (next_child) {
|
||||
if (next_child->data.RI) {
|
||||
break;
|
||||
Window *win = next_child == nullptr ? nullptr : next_child->data.parent_window;
|
||||
if (win) { // Cycle through `Control` subtrees of the parent window
|
||||
if (window_next == -1) {
|
||||
window_next = next_child->get_index();
|
||||
ERR_FAIL_INDEX_V(window_next, win->get_child_count(), nullptr);
|
||||
}
|
||||
|
||||
for (int i = 1; i < win->get_child_count() + 1; i++) {
|
||||
int next = Math::wrapi(window_next + i, 0, win->get_child_count());
|
||||
Control *c = Object::cast_to<Control>(win->get_child(next));
|
||||
if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
|
||||
continue;
|
||||
}
|
||||
next_child = next_child->get_parent_control();
|
||||
window_next = next;
|
||||
next_child = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next_child == from || next_child == this) { // No next control.
|
||||
return (get_focus_mode() == FOCUS_ALL) ? next_child : nullptr;
|
||||
}
|
||||
if (next_child) {
|
||||
if (next_child->get_focus_mode() == FOCUS_ALL) {
|
||||
return next_child;
|
||||
}
|
||||
from = next_child;
|
||||
} else {
|
||||
if (!next_child) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((next_child->get_focus_mode_with_recursive() == FOCUS_ALL) || (ac_enabled && next_child->get_focus_mode_with_recursive() == FOCUS_ACCESSIBILITY)) {
|
||||
return next_child;
|
||||
}
|
||||
|
||||
if (next_child == from || next_child == this) {
|
||||
return nullptr; // Stuck in a loop with no next control.
|
||||
}
|
||||
|
||||
from = next_child; // Try to find the next control with focus mode FOCUS_ALL.
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static Control *_prev_control(Control *p_from) {
|
||||
Control *child = nullptr;
|
||||
for (int i = p_from->get_child_count() - 1; i >= 0; i--) {
|
||||
Control *c = Object::cast_to<Control>(p_from->get_child(i));
|
||||
if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
child = c;
|
||||
break;
|
||||
// Find the last child as prev, try the same in the last child.
|
||||
return _prev_control(c);
|
||||
}
|
||||
|
||||
if (!child) {
|
||||
return p_from;
|
||||
}
|
||||
|
||||
// No prev in parent, try the same in parent.
|
||||
return _prev_control(child);
|
||||
return p_from; // Not found in the children, return itself.
|
||||
}
|
||||
|
||||
Control *Control::find_prev_valid_focus() const {
|
||||
ERR_READ_THREAD_GUARD_V(nullptr);
|
||||
|
||||
// If the focus property is manually overwritten, attempt to use it.
|
||||
if (!data.focus_prev.is_empty()) {
|
||||
Node *n = get_node_or_null(data.focus_prev);
|
||||
ERR_FAIL_NULL_V_MSG(n, nullptr, "Previous focus node path is invalid: '" + data.focus_prev + "'.");
|
||||
Control *c = Object::cast_to<Control>(n);
|
||||
ERR_FAIL_NULL_V_MSG(c, nullptr, "Previous focus node is not a control: '" + n->get_name() + "'.");
|
||||
if (c->is_visible_in_tree() && c->get_focus_mode_with_recursive() != FOCUS_NONE) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
Control *from = const_cast<Control *>(this);
|
||||
bool ac_enabled = get_tree() && get_tree()->is_accessibility_enabled();
|
||||
|
||||
// Index of the current `Control` subtree within the containing `Window`.
|
||||
int window_prev = -1;
|
||||
|
||||
while (true) {
|
||||
// If the focus property is manually overwritten, attempt to use it.
|
||||
|
||||
if (!data.focus_prev.is_empty()) {
|
||||
Node *n = get_node_or_null(data.focus_prev);
|
||||
ERR_FAIL_NULL_V_MSG(n, nullptr, "Previous focus node path is invalid: '" + data.focus_prev + "'.");
|
||||
Control *c = Object::cast_to<Control>(n);
|
||||
ERR_FAIL_NULL_V_MSG(c, nullptr, "Previous focus node is not a control: '" + n->get_name() + "'.");
|
||||
if (c->is_visible() && c->get_focus_mode() != FOCUS_NONE) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
// Find prev child.
|
||||
|
||||
Control *prev_child = nullptr;
|
||||
|
||||
if (from->is_set_as_top_level() || !Object::cast_to<Control>(from->get_parent())) {
|
||||
if (from->is_set_as_top_level() || !from->data.parent_control) {
|
||||
// Find last of the children.
|
||||
|
||||
prev_child = _prev_control(from);
|
||||
Window *win = from->data.parent_window;
|
||||
if (win) { // Cycle through `Control` subtrees of the parent window
|
||||
if (window_prev == -1) {
|
||||
window_prev = from->get_index();
|
||||
ERR_FAIL_INDEX_V(window_prev, win->get_child_count(), nullptr);
|
||||
}
|
||||
|
||||
for (int i = 1; i < win->get_child_count() + 1; i++) {
|
||||
int prev = Math::wrapi(window_prev - i, 0, win->get_child_count());
|
||||
Control *c = Object::cast_to<Control>(win->get_child(prev));
|
||||
if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
|
||||
continue;
|
||||
}
|
||||
window_prev = prev;
|
||||
prev_child = _prev_control(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prev_child) {
|
||||
prev_child = _prev_control(from); // Wrap start here.
|
||||
}
|
||||
|
||||
} else {
|
||||
for (int i = (from->get_index() - 1); i >= 0; i--) {
|
||||
|
|
@ -2201,21 +2377,21 @@ Control *Control::find_prev_valid_focus() const {
|
|||
}
|
||||
|
||||
if (!prev_child) {
|
||||
prev_child = Object::cast_to<Control>(from->get_parent());
|
||||
prev_child = from->data.parent_control;
|
||||
} else {
|
||||
prev_child = _prev_control(prev_child);
|
||||
}
|
||||
}
|
||||
|
||||
if (prev_child == from || prev_child == this) { // No prev control.
|
||||
return (get_focus_mode() == FOCUS_ALL) ? prev_child : nullptr;
|
||||
}
|
||||
|
||||
if (prev_child->get_focus_mode() == FOCUS_ALL) {
|
||||
if ((prev_child->get_focus_mode_with_recursive() == FOCUS_ALL) || (ac_enabled && prev_child->get_focus_mode_with_recursive() == FOCUS_ACCESSIBILITY)) {
|
||||
return prev_child;
|
||||
}
|
||||
|
||||
from = prev_child;
|
||||
if (prev_child == from || prev_child == this) {
|
||||
return nullptr; // Stuck in a loop with no prev control.
|
||||
}
|
||||
|
||||
from = prev_child; // Try to find the prev control with focus mode FOCUS_ALL.
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
|
@ -2266,14 +2442,7 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
|
|||
ERR_FAIL_NULL_V_MSG(n, nullptr, "Neighbor focus node path is invalid: '" + data.focus_neighbor[p_side] + "'.");
|
||||
Control *c = Object::cast_to<Control>(n);
|
||||
ERR_FAIL_NULL_V_MSG(c, nullptr, "Neighbor focus node is not a control: '" + n->get_name() + "'.");
|
||||
bool valid = true;
|
||||
if (!c->is_visible()) {
|
||||
valid = false;
|
||||
}
|
||||
if (c->get_focus_mode() == FOCUS_NONE) {
|
||||
valid = false;
|
||||
}
|
||||
if (valid) {
|
||||
if (c->is_visible_in_tree() && c->get_focus_mode_with_recursive() != FOCUS_NONE) {
|
||||
return c;
|
||||
}
|
||||
|
||||
|
|
@ -2382,6 +2551,64 @@ Control *Control::_get_focus_neighbor(Side p_side, int p_count) {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool Control::_is_focus_disabled_recursively() const {
|
||||
switch (data.focus_recursive_behavior) {
|
||||
case RECURSIVE_BEHAVIOR_INHERITED:
|
||||
return data.parent_focus_recursive_behavior == RECURSIVE_BEHAVIOR_DISABLED;
|
||||
case RECURSIVE_BEHAVIOR_DISABLED:
|
||||
return true;
|
||||
case RECURSIVE_BEHAVIOR_ENABLED:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Control::_propagate_focus_behavior_recursively(RecursiveBehavior p_focus_recursive_behavior, bool p_skip_non_inherited) {
|
||||
if (is_inside_tree() && (data.focus_recursive_behavior == RECURSIVE_BEHAVIOR_DISABLED || (data.focus_recursive_behavior == RECURSIVE_BEHAVIOR_INHERITED && p_focus_recursive_behavior == RECURSIVE_BEHAVIOR_DISABLED)) && has_focus()) {
|
||||
release_focus();
|
||||
}
|
||||
|
||||
if (p_skip_non_inherited && data.focus_recursive_behavior != RECURSIVE_BEHAVIOR_INHERITED) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.parent_focus_recursive_behavior = p_focus_recursive_behavior;
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *control = Object::cast_to<Control>(get_child(i));
|
||||
if (control) {
|
||||
control->_propagate_focus_behavior_recursively(p_focus_recursive_behavior, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Control::_is_parent_mouse_disabled() const {
|
||||
switch (data.mouse_recursive_behavior) {
|
||||
case RECURSIVE_BEHAVIOR_INHERITED:
|
||||
return data.parent_mouse_recursive_behavior == RECURSIVE_BEHAVIOR_DISABLED;
|
||||
case RECURSIVE_BEHAVIOR_DISABLED:
|
||||
return true;
|
||||
case RECURSIVE_BEHAVIOR_ENABLED:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Control::_propagate_mouse_behavior_recursively(RecursiveBehavior p_mouse_recursive_behavior, bool p_skip_non_inherited) {
|
||||
if (p_skip_non_inherited && data.mouse_recursive_behavior != RECURSIVE_BEHAVIOR_INHERITED) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.parent_mouse_recursive_behavior = p_mouse_recursive_behavior;
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *control = Object::cast_to<Control>(get_child(i));
|
||||
if (control) {
|
||||
control->_propagate_mouse_behavior_recursively(p_mouse_recursive_behavior, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Control *Control::find_valid_focus_neighbor(Side p_side) const {
|
||||
return const_cast<Control *>(this)->_get_focus_neighbor(p_side);
|
||||
}
|
||||
|
|
@ -2391,11 +2618,13 @@ void Control::_window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, cons
|
|||
return; // Bye.
|
||||
}
|
||||
|
||||
bool ac_enabled = get_tree() && get_tree()->is_accessibility_enabled();
|
||||
|
||||
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 && !in_container && p_clamp.intersects(c->get_global_rect())) {
|
||||
if (c && c != this && ((c->get_focus_mode_with_recursive() == FOCUS_ALL) || (ac_enabled && c->get_focus_mode_with_recursive() == FOCUS_ACCESSIBILITY)) && !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());
|
||||
|
|
@ -2808,6 +3037,44 @@ Variant Control::get_theme_item(Theme::DataType p_data_type, const StringName &p
|
|||
return Variant();
|
||||
}
|
||||
|
||||
Variant Control::get_used_theme_item(const String &p_full_name, const StringName &p_theme_type) const {
|
||||
if (p_full_name.begins_with("theme_override_icons/")) {
|
||||
String name = p_full_name.substr(strlen("theme_override_icons/"));
|
||||
if (has_theme_icon(name)) { // Exclude cached and default ones.
|
||||
return get_theme_icon(name);
|
||||
}
|
||||
} else if (p_full_name.begins_with("theme_override_styles/")) {
|
||||
String name = p_full_name.substr(strlen("theme_override_styles/"));
|
||||
if (has_theme_stylebox(name)) {
|
||||
return get_theme_stylebox(name);
|
||||
}
|
||||
} else if (p_full_name.begins_with("theme_override_fonts/")) {
|
||||
String name = p_full_name.substr(strlen("theme_override_fonts/"));
|
||||
if (has_theme_font(name)) {
|
||||
return get_theme_font(name);
|
||||
}
|
||||
} else if (p_full_name.begins_with("theme_override_font_sizes/")) {
|
||||
String name = p_full_name.substr(strlen("theme_override_font_sizes/"));
|
||||
if (has_theme_font_size(name)) {
|
||||
return get_theme_font_size(name);
|
||||
}
|
||||
} else if (p_full_name.begins_with("theme_override_colors/")) {
|
||||
String name = p_full_name.substr(strlen("theme_override_colors/"));
|
||||
if (has_theme_color(name)) {
|
||||
return get_theme_color(name);
|
||||
}
|
||||
} else if (p_full_name.begins_with("theme_override_constants/")) {
|
||||
String name = p_full_name.substr(strlen("theme_override_constants/"));
|
||||
if (has_theme_constant(name)) {
|
||||
return get_theme_constant(name);
|
||||
}
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(Variant(), vformat("The property %s is not a theme item.", p_full_name));
|
||||
}
|
||||
|
||||
return Variant();
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Ref<Texture2D> Control::get_editor_theme_icon(const StringName &p_name) const {
|
||||
return get_theme_icon(p_name, SNAME("EditorIcons"));
|
||||
|
|
@ -3263,6 +3530,13 @@ String Control::get_tooltip(const Point2 &p_pos) const {
|
|||
return data.tooltip;
|
||||
}
|
||||
|
||||
String Control::accessibility_get_contextual_info() const {
|
||||
ERR_READ_THREAD_GUARD_V(String());
|
||||
String ret;
|
||||
GDVIRTUAL_CALL(_accessibility_get_contextual_info, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Control *Control::make_custom_tooltip(const String &p_text) const {
|
||||
ERR_READ_THREAD_GUARD_V(nullptr);
|
||||
Object *ret = nullptr;
|
||||
|
|
@ -3272,6 +3546,35 @@ Control *Control::make_custom_tooltip(const String &p_text) const {
|
|||
|
||||
// Base object overrides.
|
||||
|
||||
void Control::_accessibility_action_foucs(const Variant &p_data) {
|
||||
grab_focus();
|
||||
}
|
||||
|
||||
void Control::_accessibility_action_blur(const Variant &p_data) {
|
||||
release_focus();
|
||||
}
|
||||
|
||||
void Control::_accessibility_action_show_tooltip(const Variant &p_data) {
|
||||
Viewport *vp = get_viewport();
|
||||
if (vp) {
|
||||
vp->show_tooltip(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Control::_accessibility_action_hide_tooltip(const Variant &p_data) {
|
||||
Viewport *vp = get_viewport();
|
||||
if (vp) {
|
||||
vp->cancel_tooltip();
|
||||
}
|
||||
}
|
||||
|
||||
void Control::_accessibility_action_scroll_into_view(const Variant &p_data) {
|
||||
ScrollContainer *sc = Object::cast_to<ScrollContainer>(get_parent());
|
||||
if (sc) {
|
||||
sc->ensure_control_visible(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Control::_notification(int p_notification) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
switch (p_notification) {
|
||||
|
|
@ -3283,6 +3586,31 @@ void Control::_notification(int p_notification) {
|
|||
saving = false;
|
||||
} break;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_transform(ae, get_transform());
|
||||
DisplayServer::get_singleton()->accessibility_update_set_bounds(ae, Rect2(Vector2(), data.size_cache));
|
||||
DisplayServer::get_singleton()->accessibility_update_set_tooltip(ae, data.tooltip);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(ae, DisplayServer::AccessibilityFlags::FLAG_CLIPS_CHILDREN, data.clip_contents);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(ae, DisplayServer::AccessibilityFlags::FLAG_TOUCH_PASSTHROUGH, data.mouse_filter == MOUSE_FILTER_PASS);
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_FOCUS, callable_mp(this, &Control::_accessibility_action_foucs));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_BLUR, callable_mp(this, &Control::_accessibility_action_blur));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SHOW_TOOLTIP, callable_mp(this, &Control::_accessibility_action_show_tooltip));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_HIDE_TOOLTIP, callable_mp(this, &Control::_accessibility_action_hide_tooltip));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_INTO_VIEW, callable_mp(this, &Control::_accessibility_action_scroll_into_view));
|
||||
if (is_inside_tree() && get_viewport()->gui_is_dragging()) {
|
||||
if (can_drop_data(Vector2(Math::INF, Math::INF), get_viewport()->gui_get_drag_data())) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_extra_info(ae, vformat(RTR("%s can be dropped here. Use %s to drop, use %s to cancel."), get_viewport()->gui_get_drag_description(), InputMap::get_singleton()->get_action_description("ui_accessibility_drag_and_drop"), InputMap::get_singleton()->get_action_description("ui_cancel")));
|
||||
} else {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_extra_info(ae, vformat(RTR("%s can not be dropped here. Use %s to cancel."), get_viewport()->gui_get_drag_description(), InputMap::get_singleton()->get_action_description("ui_cancel")));
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_POSTINITIALIZE: {
|
||||
data.initialized = true;
|
||||
|
||||
|
|
@ -3298,6 +3626,9 @@ void Control::_notification(int p_notification) {
|
|||
data.theme_owner->assign_theme_on_parented(this);
|
||||
|
||||
_update_layout_mode();
|
||||
|
||||
_set_focus_recursive_behavior_ignore_cache(data.focus_recursive_behavior);
|
||||
_set_mouse_recursive_behavior_ignore_cache(data.mouse_recursive_behavior);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_UNPARENTED: {
|
||||
|
|
@ -3518,6 +3849,9 @@ void Control::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_global_rect"), &Control::get_global_rect);
|
||||
ClassDB::bind_method(D_METHOD("set_focus_mode", "mode"), &Control::set_focus_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_focus_mode"), &Control::get_focus_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_focus_mode_with_recursive"), &Control::get_focus_mode_with_recursive);
|
||||
ClassDB::bind_method(D_METHOD("set_focus_recursive_behavior", "focus_recursive_behavior"), &Control::set_focus_recursive_behavior);
|
||||
ClassDB::bind_method(D_METHOD("get_focus_recursive_behavior"), &Control::get_focus_recursive_behavior);
|
||||
ClassDB::bind_method(D_METHOD("has_focus"), &Control::has_focus);
|
||||
ClassDB::bind_method(D_METHOD("grab_focus"), &Control::grab_focus);
|
||||
ClassDB::bind_method(D_METHOD("release_focus"), &Control::release_focus);
|
||||
|
|
@ -3611,8 +3945,15 @@ void Control::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("force_drag", "data", "preview"), &Control::force_drag);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("accessibility_drag"), &Control::accessibility_drag);
|
||||
ClassDB::bind_method(D_METHOD("accessibility_drop"), &Control::accessibility_drop);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_mouse_filter", "filter"), &Control::set_mouse_filter);
|
||||
ClassDB::bind_method(D_METHOD("get_mouse_filter"), &Control::get_mouse_filter);
|
||||
ClassDB::bind_method(D_METHOD("get_mouse_filter_with_recursive"), &Control::get_mouse_filter_with_recursive);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_mouse_recursive_behavior", "mouse_recursive_behavior"), &Control::set_mouse_recursive_behavior);
|
||||
ClassDB::bind_method(D_METHOD("get_mouse_recursive_behavior"), &Control::get_mouse_recursive_behavior);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_force_pass_scroll_events", "force_pass_scroll_events"), &Control::set_force_pass_scroll_events);
|
||||
ClassDB::bind_method(D_METHOD("is_force_pass_scroll_events"), &Control::is_force_pass_scroll_events);
|
||||
|
|
@ -3708,10 +4049,12 @@ void Control::_bind_methods() {
|
|||
ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_bottom", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_BOTTOM);
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_next", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_next", "get_focus_next");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "focus_previous", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_previous", "get_focus_previous");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All,Accessibility"), "set_focus_mode", "get_focus_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_recursive_behavior", PROPERTY_HINT_ENUM, "Inherited,Disabled,Enabled"), "set_focus_recursive_behavior", "get_focus_recursive_behavior");
|
||||
|
||||
ADD_GROUP("Mouse", "mouse_");
|
||||
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::INT, "mouse_recursive_behavior", PROPERTY_HINT_ENUM, "Inherited,Disabled,Enabled"), "set_mouse_recursive_behavior", "get_mouse_recursive_behavior");
|
||||
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");
|
||||
|
||||
|
|
@ -3725,6 +4068,11 @@ void Control::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(FOCUS_NONE);
|
||||
BIND_ENUM_CONSTANT(FOCUS_CLICK);
|
||||
BIND_ENUM_CONSTANT(FOCUS_ALL);
|
||||
BIND_ENUM_CONSTANT(FOCUS_ACCESSIBILITY);
|
||||
|
||||
BIND_ENUM_CONSTANT(RECURSIVE_BEHAVIOR_INHERITED);
|
||||
BIND_ENUM_CONSTANT(RECURSIVE_BEHAVIOR_DISABLED);
|
||||
BIND_ENUM_CONSTANT(RECURSIVE_BEHAVIOR_ENABLED);
|
||||
|
||||
BIND_CONSTANT(NOTIFICATION_RESIZED);
|
||||
BIND_CONSTANT(NOTIFICATION_MOUSE_ENTER);
|
||||
|
|
@ -3831,6 +4179,8 @@ void Control::_bind_methods() {
|
|||
GDVIRTUAL_BIND(_drop_data, "at_position", "data");
|
||||
GDVIRTUAL_BIND(_make_custom_tooltip, "for_text");
|
||||
|
||||
GDVIRTUAL_BIND(_accessibility_get_contextual_info);
|
||||
|
||||
GDVIRTUAL_BIND(_gui_input, "event");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef CONTROL_H
|
||||
#define CONTROL_H
|
||||
#pragma once
|
||||
|
||||
#include "core/math/transform_2d.h"
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
|
|
@ -65,7 +64,14 @@ public:
|
|||
enum FocusMode {
|
||||
FOCUS_NONE,
|
||||
FOCUS_CLICK,
|
||||
FOCUS_ALL
|
||||
FOCUS_ALL,
|
||||
FOCUS_ACCESSIBILITY,
|
||||
};
|
||||
|
||||
enum RecursiveBehavior {
|
||||
RECURSIVE_BEHAVIOR_INHERITED,
|
||||
RECURSIVE_BEHAVIOR_DISABLED,
|
||||
RECURSIVE_BEHAVIOR_ENABLED,
|
||||
};
|
||||
|
||||
enum SizeFlags {
|
||||
|
|
@ -191,6 +197,8 @@ private:
|
|||
real_t offset[4] = { 0.0, 0.0, 0.0, 0.0 };
|
||||
real_t anchor[4] = { ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN, ANCHOR_BEGIN };
|
||||
FocusMode focus_mode = FOCUS_NONE;
|
||||
RecursiveBehavior parent_focus_recursive_behavior = RECURSIVE_BEHAVIOR_INHERITED;
|
||||
RecursiveBehavior focus_recursive_behavior = RECURSIVE_BEHAVIOR_INHERITED;
|
||||
GrowDirection h_grow = GROW_DIRECTION_END;
|
||||
GrowDirection v_grow = GROW_DIRECTION_END;
|
||||
|
||||
|
|
@ -219,6 +227,8 @@ private:
|
|||
// Input events and rendering.
|
||||
|
||||
MouseFilter mouse_filter = MOUSE_FILTER_STOP;
|
||||
RecursiveBehavior parent_mouse_recursive_behavior = RECURSIVE_BEHAVIOR_INHERITED;
|
||||
RecursiveBehavior mouse_recursive_behavior = RECURSIVE_BEHAVIOR_INHERITED;
|
||||
bool force_pass_scroll_events = true;
|
||||
|
||||
bool clip_contents = false;
|
||||
|
|
@ -291,6 +301,7 @@ private:
|
|||
|
||||
void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]);
|
||||
void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
|
||||
void _compute_edge_positions(Rect2 p_rect, real_t (&r_edge_positions)[4]);
|
||||
|
||||
void _set_layout_mode(LayoutMode p_mode);
|
||||
void _update_layout_mode();
|
||||
|
|
@ -312,10 +323,19 @@ private:
|
|||
|
||||
void _call_gui_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
// Mouse Filter.
|
||||
|
||||
bool _is_parent_mouse_disabled() const;
|
||||
|
||||
// Focus.
|
||||
|
||||
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);
|
||||
bool _is_focus_disabled_recursively() const;
|
||||
void _propagate_focus_behavior_recursively(RecursiveBehavior p_focus_recursive_behavior, bool p_force);
|
||||
void _propagate_mouse_behavior_recursively(RecursiveBehavior p_focus_recursive_behavior, bool p_force);
|
||||
void _set_mouse_recursive_behavior_ignore_cache(RecursiveBehavior p_recursive_mouse_behavior);
|
||||
void _set_focus_recursive_behavior_ignore_cache(RecursiveBehavior p_recursive_mouse_behavior);
|
||||
|
||||
// Theming.
|
||||
|
||||
|
|
@ -327,8 +347,6 @@ private:
|
|||
|
||||
static int root_layout_direction;
|
||||
|
||||
String get_tooltip_text() const;
|
||||
|
||||
protected:
|
||||
// Dynamic properties.
|
||||
|
||||
|
|
@ -353,6 +371,12 @@ protected:
|
|||
void _notification(int p_notification);
|
||||
static void _bind_methods();
|
||||
|
||||
void _accessibility_action_foucs(const Variant &p_data);
|
||||
void _accessibility_action_blur(const Variant &p_data);
|
||||
void _accessibility_action_show_tooltip(const Variant &p_data);
|
||||
void _accessibility_action_hide_tooltip(const Variant &p_data);
|
||||
void _accessibility_action_scroll_into_view(const Variant &p_data);
|
||||
|
||||
// Exposed virtual methods.
|
||||
|
||||
GDVIRTUAL1RC(bool, _has_point, Vector2)
|
||||
|
|
@ -365,6 +389,8 @@ protected:
|
|||
GDVIRTUAL2(_drop_data, Vector2, Variant)
|
||||
GDVIRTUAL1RC(Object *, _make_custom_tooltip, String)
|
||||
|
||||
GDVIRTUAL0RC(String, _accessibility_get_contextual_info);
|
||||
|
||||
GDVIRTUAL1(_gui_input, Ref<InputEvent>)
|
||||
|
||||
public:
|
||||
|
|
@ -420,6 +446,7 @@ public:
|
|||
static void set_root_layout_direction(int p_root_dir);
|
||||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
PackedStringArray get_accessibility_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 //TOOLS_ENABLED
|
||||
|
|
@ -428,8 +455,6 @@ public:
|
|||
|
||||
// Global relations.
|
||||
|
||||
bool is_top_level_control() const;
|
||||
|
||||
Control *get_parent_control() const;
|
||||
Window *get_parent_window() const;
|
||||
Control *get_root_parent_control() const;
|
||||
|
|
@ -516,6 +541,10 @@ public:
|
|||
|
||||
void set_mouse_filter(MouseFilter p_filter);
|
||||
MouseFilter get_mouse_filter() const;
|
||||
MouseFilter get_mouse_filter_with_recursive() const;
|
||||
|
||||
void set_mouse_recursive_behavior(RecursiveBehavior p_recursive_mouse_behavior);
|
||||
RecursiveBehavior get_mouse_recursive_behavior() const;
|
||||
|
||||
void set_force_pass_scroll_events(bool p_force_pass_scroll_events);
|
||||
bool is_force_pass_scroll_events() const;
|
||||
|
|
@ -534,12 +563,17 @@ public:
|
|||
virtual void drop_data(const Point2 &p_point, const Variant &p_data);
|
||||
void set_drag_preview(Control *p_control);
|
||||
void force_drag(const Variant &p_data, Control *p_control);
|
||||
void accessibility_drag();
|
||||
void accessibility_drop();
|
||||
bool is_drag_successful() const;
|
||||
|
||||
// Focus.
|
||||
|
||||
void set_focus_mode(FocusMode p_focus_mode);
|
||||
FocusMode get_focus_mode() const;
|
||||
FocusMode get_focus_mode_with_recursive() const;
|
||||
void set_focus_recursive_behavior(RecursiveBehavior p_recursive_mouse_behavior);
|
||||
RecursiveBehavior get_focus_recursive_behavior() const;
|
||||
bool has_focus() const;
|
||||
void grab_focus();
|
||||
void grab_click_focus();
|
||||
|
|
@ -607,6 +641,7 @@ public:
|
|||
Color get_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
int get_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
Variant get_theme_item(Theme::DataType p_data_type, const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
Variant get_used_theme_item(const String &p_full_name, const StringName &p_theme_type = StringName()) const;
|
||||
#ifdef TOOLS_ENABLED
|
||||
Ref<Texture2D> get_editor_theme_icon(const StringName &p_name) const;
|
||||
#endif //TOOLS_ENABLED
|
||||
|
|
@ -648,15 +683,19 @@ public:
|
|||
|
||||
// Extra properties.
|
||||
|
||||
String get_tooltip_text() const;
|
||||
void set_tooltip_text(const String &text);
|
||||
virtual String get_tooltip(const Point2 &p_pos) const;
|
||||
virtual Control *make_custom_tooltip(const String &p_text) const;
|
||||
|
||||
virtual String accessibility_get_contextual_info() const;
|
||||
|
||||
Control();
|
||||
~Control();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Control::FocusMode);
|
||||
VARIANT_ENUM_CAST(Control::RecursiveBehavior);
|
||||
VARIANT_BITFIELD_CAST(Control::SizeFlags);
|
||||
VARIANT_ENUM_CAST(Control::CursorShape);
|
||||
VARIANT_ENUM_CAST(Control::LayoutPreset);
|
||||
|
|
@ -673,5 +712,3 @@ VARIANT_ENUM_CAST(Control::TextDirection);
|
|||
#define SET_DRAG_FORWARDING_CDU(from, to) from->set_drag_forwarding(Callable(), callable_mp(this, &to::_can_drop_data_fw).bind(from), callable_mp(this, &to::_drop_data_fw).bind(from));
|
||||
#define SET_DRAG_FORWARDING_GCD(from, to) from->set_drag_forwarding(callable_mp(this, &to::get_drag_data_fw).bind(from), callable_mp(this, &to::can_drop_data_fw).bind(from), callable_mp(this, &to::drop_data_fw).bind(from));
|
||||
#define SET_DRAG_FORWARDING_GCDU(from, to) from->set_drag_forwarding(callable_mp(this, &to::_get_drag_data_fw).bind(from), callable_mp(this, &to::_can_drop_data_fw).bind(from), callable_mp(this, &to::_drop_data_fw).bind(from));
|
||||
|
||||
#endif // CONTROL_H
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
#include "scene/gui/line_edit.h"
|
||||
|
||||
void AcceptDialog::_register_text_enter_bind_compat_89419(Control *p_line_edit) {
|
||||
register_text_enter(Object::cast_to<LineEdit>(p_line_edit));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,12 @@ void AcceptDialog::_parent_focused() {
|
|||
|
||||
void AcceptDialog::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_DIALOG);
|
||||
} break;
|
||||
case NOTIFICATION_POST_ENTER_TREE: {
|
||||
if (is_visible()) {
|
||||
get_ok_button()->grab_focus();
|
||||
|
|
@ -197,16 +203,12 @@ bool AcceptDialog::has_autowrap() {
|
|||
}
|
||||
|
||||
void AcceptDialog::set_ok_button_text(String p_ok_button_text) {
|
||||
ok_button->set_text(p_ok_button_text);
|
||||
|
||||
child_controls_changed();
|
||||
if (is_visible()) {
|
||||
_update_child_rects();
|
||||
}
|
||||
ok_text = p_ok_button_text;
|
||||
_update_ok_text();
|
||||
}
|
||||
|
||||
String AcceptDialog::get_ok_button_text() const {
|
||||
return ok_button->get_text();
|
||||
return ok_text;
|
||||
}
|
||||
|
||||
void AcceptDialog::register_text_enter(LineEdit *p_line_edit) {
|
||||
|
|
@ -257,6 +259,25 @@ void AcceptDialog::_update_child_rects() {
|
|||
}
|
||||
}
|
||||
|
||||
void AcceptDialog::_update_ok_text() {
|
||||
String prev_text = ok_button->get_text();
|
||||
String new_text = internal_ok_text;
|
||||
|
||||
if (!ok_text.is_empty()) {
|
||||
new_text = ok_text;
|
||||
}
|
||||
|
||||
if (new_text == prev_text) {
|
||||
return;
|
||||
}
|
||||
ok_button->set_text(new_text);
|
||||
|
||||
child_controls_changed();
|
||||
if (is_visible()) {
|
||||
_update_child_rects();
|
||||
}
|
||||
}
|
||||
|
||||
Size2 AcceptDialog::_get_contents_minimum_size() const {
|
||||
// First, we then iterate over the label and any other custom controls
|
||||
// to try and find the size that encompasses all content.
|
||||
|
|
@ -294,6 +315,11 @@ Size2 AcceptDialog::_get_contents_minimum_size() const {
|
|||
return content_minsize;
|
||||
}
|
||||
|
||||
void AcceptDialog::set_internal_ok_text(const String &p_text) {
|
||||
internal_ok_text = p_text;
|
||||
_update_ok_text();
|
||||
}
|
||||
|
||||
void AcceptDialog::_custom_action(const String &p_action) {
|
||||
emit_signal(SNAME("custom_action"), p_action);
|
||||
custom_action(p_action);
|
||||
|
|
@ -428,6 +454,9 @@ AcceptDialog::AcceptDialog() {
|
|||
set_clamp_to_embedder(true);
|
||||
set_keep_title_visible(true);
|
||||
|
||||
set_flag(FLAG_MINIMIZE_DISABLED, true);
|
||||
set_flag(FLAG_MAXIMIZE_DISABLED, true);
|
||||
|
||||
bg_panel = memnew(Panel);
|
||||
add_child(bg_panel, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
|
|
@ -442,7 +471,7 @@ AcceptDialog::AcceptDialog() {
|
|||
|
||||
buttons_hbox->add_spacer();
|
||||
ok_button = memnew(Button);
|
||||
ok_button->set_text(ETR("OK"));
|
||||
set_internal_ok_text(ETR("OK"));
|
||||
buttons_hbox->add_child(ok_button);
|
||||
buttons_hbox->add_spacer();
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DIALOGS_H
|
||||
#define DIALOGS_H
|
||||
#pragma once
|
||||
|
||||
#include "box_container.h"
|
||||
#include "scene/gui/button.h"
|
||||
|
|
@ -52,6 +51,9 @@ class AcceptDialog : public Window {
|
|||
Button *ok_button = nullptr;
|
||||
|
||||
bool popped_up = false;
|
||||
String ok_text;
|
||||
String internal_ok_text;
|
||||
|
||||
bool hide_on_ok = true;
|
||||
bool close_on_escape = true;
|
||||
|
||||
|
|
@ -65,6 +67,7 @@ class AcceptDialog : public Window {
|
|||
void _custom_action(const String &p_action);
|
||||
void _custom_button_visibility_changed(Button *button);
|
||||
void _update_child_rects();
|
||||
void _update_ok_text();
|
||||
|
||||
static bool swap_cancel_ok;
|
||||
|
||||
|
|
@ -82,6 +85,8 @@ protected:
|
|||
virtual void cancel_pressed() {}
|
||||
virtual void custom_action(const String &) {}
|
||||
|
||||
void set_internal_ok_text(const String &p_text);
|
||||
|
||||
// Not private since used by derived classes signal.
|
||||
void _text_submitted(const String &p_text);
|
||||
void _ok_pressed();
|
||||
|
|
@ -139,5 +144,3 @@ public:
|
|||
|
||||
ConfirmationDialog();
|
||||
};
|
||||
|
||||
#endif // DIALOGS_H
|
||||
|
|
|
|||
|
|
@ -68,10 +68,18 @@ void FileDialog::_native_popup() {
|
|||
} else if (access == ACCESS_USERDATA) {
|
||||
root = OS::get_singleton()->get_user_data_dir();
|
||||
}
|
||||
|
||||
// Attach native file dialog to first persistent parent window.
|
||||
Window *w = (is_transient() || is_transient_to_focused()) ? get_parent_visible_window() : nullptr;
|
||||
while (w && w->get_flag(FLAG_POPUP) && w->get_parent_visible_window()) {
|
||||
w = w->get_parent_visible_window();
|
||||
}
|
||||
DisplayServer::WindowID wid = w ? w->get_window_id() : DisplayServer::INVALID_WINDOW_ID;
|
||||
|
||||
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));
|
||||
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), wid);
|
||||
} 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));
|
||||
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), wid);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,9 +168,9 @@ void FileDialog::_native_dialog_cb_with_options(bool p_ok, const Vector<String>
|
|||
} 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);
|
||||
String flt = filters[i].get_slicec(';', 0);
|
||||
for (int j = 0; j < flt.get_slice_count(","); j++) {
|
||||
String str = flt.get_slice(",", j).strip_edges();
|
||||
String str = flt.get_slicec(',', j).strip_edges();
|
||||
if (f.matchn(str)) {
|
||||
valid = true;
|
||||
break;
|
||||
|
|
@ -178,10 +186,10 @@ void FileDialog::_native_dialog_cb_with_options(bool p_ok, const Vector<String>
|
|||
idx--;
|
||||
}
|
||||
if (idx >= 0 && idx < filters.size()) {
|
||||
String flt = filters[idx].get_slice(";", 0);
|
||||
String flt = filters[idx].get_slicec(';', 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());
|
||||
String str = flt.get_slicec(',', j).strip_edges();
|
||||
if (f.matchn(str)) {
|
||||
valid = true;
|
||||
break;
|
||||
|
|
@ -189,8 +197,8 @@ void FileDialog::_native_dialog_cb_with_options(bool p_ok, const Vector<String>
|
|||
}
|
||||
|
||||
if (!valid && filter_slice_count > 0) {
|
||||
String str = (flt.get_slice(",", 0).strip_edges());
|
||||
f += str.substr(1, str.length() - 1);
|
||||
String str = flt.get_slicec(',', 0).strip_edges();
|
||||
f += str.substr(1);
|
||||
file->set_text(f.get_file());
|
||||
valid = true;
|
||||
}
|
||||
|
|
@ -202,8 +210,8 @@ void FileDialog::_native_dialog_cb_with_options(bool p_ok, const Vector<String>
|
|||
// 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();
|
||||
String flt = filters[idx].get_slicec(';', 0);
|
||||
String ext = flt.get_slicec(',', 0).strip_edges().get_extension();
|
||||
f += "." + ext;
|
||||
}
|
||||
emit_signal(SNAME("file_selected"), f);
|
||||
|
|
@ -478,7 +486,7 @@ void FileDialog::_post_popup() {
|
|||
void FileDialog::_push_history() {
|
||||
local_history.resize(local_history_pos + 1);
|
||||
String new_path = dir_access->get_current_dir();
|
||||
if (local_history.size() == 0 || new_path != local_history[local_history_pos]) {
|
||||
if (local_history.is_empty() || new_path != local_history[local_history_pos]) {
|
||||
local_history.push_back(new_path);
|
||||
local_history_pos++;
|
||||
dir_prev->set_disabled(local_history_pos == 0);
|
||||
|
|
@ -514,7 +522,7 @@ void FileDialog::_action_pressed() {
|
|||
} else if (mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_DIR) {
|
||||
String path = dir_access->get_current_dir();
|
||||
|
||||
path = path.replace("\\", "/");
|
||||
path = path.replace_char('\\', '/');
|
||||
TreeItem *item = tree->get_selected();
|
||||
if (item) {
|
||||
Dictionary d = item->get_metadata(0);
|
||||
|
|
@ -535,9 +543,9 @@ void FileDialog::_action_pressed() {
|
|||
} else if (filters.size() > 1 && filter->get_selected() == 0) {
|
||||
// Match all filters.
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
String flt = filters[i].get_slice(";", 0);
|
||||
String flt = filters[i].get_slicec(';', 0);
|
||||
for (int j = 0; j < flt.get_slice_count(","); j++) {
|
||||
String str = flt.get_slice(",", j).strip_edges();
|
||||
String str = flt.get_slicec(',', j).strip_edges();
|
||||
if (f.matchn(str)) {
|
||||
valid = true;
|
||||
break;
|
||||
|
|
@ -553,10 +561,10 @@ void FileDialog::_action_pressed() {
|
|||
idx--;
|
||||
}
|
||||
if (idx >= 0 && idx < filters.size()) {
|
||||
String flt = filters[idx].get_slice(";", 0);
|
||||
String flt = filters[idx].get_slicec(';', 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());
|
||||
String str = (flt.get_slicec(',', j).strip_edges());
|
||||
if (f.matchn(str)) {
|
||||
valid = true;
|
||||
break;
|
||||
|
|
@ -564,8 +572,8 @@ void FileDialog::_action_pressed() {
|
|||
}
|
||||
|
||||
if (!valid && filter_slice_count > 0) {
|
||||
String str = (flt.get_slice(",", 0).strip_edges());
|
||||
f += str.substr(1, str.length() - 1);
|
||||
String str = flt.get_slicec(',', 0).strip_edges();
|
||||
f += str.substr(1);
|
||||
file->set_text(f.get_file());
|
||||
valid = true;
|
||||
}
|
||||
|
|
@ -661,10 +669,10 @@ void FileDialog::deselect_all() {
|
|||
switch (mode) {
|
||||
case FILE_MODE_OPEN_FILE:
|
||||
case FILE_MODE_OPEN_FILES:
|
||||
set_ok_button_text(ETR("Open"));
|
||||
set_internal_ok_text(ETR("Open"));
|
||||
break;
|
||||
case FILE_MODE_OPEN_DIR:
|
||||
set_ok_button_text(ETR("Select Current Folder"));
|
||||
set_internal_ok_text(ETR("Select Current Folder"));
|
||||
break;
|
||||
case FILE_MODE_OPEN_ANY:
|
||||
set_ok_button_text(ETR("Open"));
|
||||
|
|
@ -690,14 +698,14 @@ void FileDialog::_tree_selected() {
|
|||
if (!d["dir"]) {
|
||||
file->set_text(d["name"]);
|
||||
if (mode == FILE_MODE_SAVE_FILE) {
|
||||
set_ok_button_text(ETR("Save"));
|
||||
set_internal_ok_text(ETR("Save"));
|
||||
} else {
|
||||
set_ok_button_text(ETR("Open"));
|
||||
set_internal_ok_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"));
|
||||
set_internal_ok_text(ETR("Select This Folder"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -862,9 +870,9 @@ void FileDialog::update_file_list() {
|
|||
} else if (filters.size() > 1 && filter->get_selected() == 0) {
|
||||
// match all filters
|
||||
for (int i = 0; i < filters.size(); i++) {
|
||||
String f = filters[i].get_slice(";", 0);
|
||||
String f = filters[i].get_slicec(';', 0);
|
||||
for (int j = 0; j < f.get_slice_count(","); j++) {
|
||||
patterns.push_back(f.get_slice(",", j).strip_edges());
|
||||
patterns.push_back(f.get_slicec(',', j).strip_edges());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -874,9 +882,9 @@ void FileDialog::update_file_list() {
|
|||
}
|
||||
|
||||
if (idx >= 0 && idx < filters.size()) {
|
||||
String f = filters[idx].get_slice(";", 0);
|
||||
String f = filters[idx].get_slicec(';', 0);
|
||||
for (int j = 0; j < f.get_slice_count(","); j++) {
|
||||
patterns.push_back(f.get_slice(",", j).strip_edges());
|
||||
patterns.push_back(f.get_slicec(',', j).strip_edges());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1184,7 +1192,7 @@ void FileDialog::set_current_path(const String &p_path) {
|
|||
set_current_file(p_path);
|
||||
} else {
|
||||
String path_dir = p_path.substr(0, pos);
|
||||
String path_file = p_path.substr(pos + 1, p_path.length());
|
||||
String path_file = p_path.substr(pos + 1);
|
||||
set_current_dir(path_dir);
|
||||
set_current_file(path_file);
|
||||
}
|
||||
|
|
@ -1227,35 +1235,35 @@ void FileDialog::set_file_mode(FileMode p_mode) {
|
|||
mode = p_mode;
|
||||
switch (mode) {
|
||||
case FILE_MODE_OPEN_FILE:
|
||||
set_ok_button_text(ETR("Open"));
|
||||
set_internal_ok_text(ETR("Open"));
|
||||
if (mode_overrides_title) {
|
||||
set_title(ETR("Open a File"));
|
||||
}
|
||||
makedir->hide();
|
||||
break;
|
||||
case FILE_MODE_OPEN_FILES:
|
||||
set_ok_button_text(ETR("Open"));
|
||||
set_internal_ok_text(ETR("Open"));
|
||||
if (mode_overrides_title) {
|
||||
set_title(ETR("Open File(s)"));
|
||||
}
|
||||
makedir->hide();
|
||||
break;
|
||||
case FILE_MODE_OPEN_DIR:
|
||||
set_ok_button_text(ETR("Select Current Folder"));
|
||||
set_internal_ok_text(ETR("Select Current Folder"));
|
||||
if (mode_overrides_title) {
|
||||
set_title(ETR("Open a Directory"));
|
||||
}
|
||||
makedir->show();
|
||||
break;
|
||||
case FILE_MODE_OPEN_ANY:
|
||||
set_ok_button_text(ETR("Open"));
|
||||
set_internal_ok_text(ETR("Open"));
|
||||
if (mode_overrides_title) {
|
||||
set_title(ETR("Open a File or Directory"));
|
||||
}
|
||||
makedir->show();
|
||||
break;
|
||||
case FILE_MODE_SAVE_FILE:
|
||||
set_ok_button_text(ETR("Save"));
|
||||
set_internal_ok_text(ETR("Save"));
|
||||
if (mode_overrides_title) {
|
||||
set_title(ETR("Save a File"));
|
||||
}
|
||||
|
|
@ -1434,9 +1442,11 @@ void FileDialog::_update_option_controls() {
|
|||
for (const FileDialog::Option &opt : options) {
|
||||
Label *lbl = memnew(Label);
|
||||
lbl->set_text(opt.name);
|
||||
lbl->set_focus_mode(Control::FOCUS_NONE);
|
||||
grid_options->add_child(lbl);
|
||||
if (opt.values.is_empty()) {
|
||||
CheckBox *cb = memnew(CheckBox);
|
||||
cb->set_accessibility_name(opt.name);
|
||||
cb->set_pressed(opt.default_idx);
|
||||
grid_options->add_child(cb);
|
||||
cb->connect(SceneStringName(toggled), callable_mp(this, &FileDialog::_option_changed_checkbox_toggled).bind(opt.name));
|
||||
|
|
@ -1446,6 +1456,7 @@ void FileDialog::_update_option_controls() {
|
|||
for (const String &val : opt.values) {
|
||||
ob->add_item(val);
|
||||
}
|
||||
ob->set_accessibility_name(opt.name);
|
||||
ob->select(opt.default_idx);
|
||||
grid_options->add_child(ob);
|
||||
ob->connect(SceneStringName(item_selected), callable_mp(this, &FileDialog::_option_changed_item_selected).bind(opt.name));
|
||||
|
|
@ -1724,11 +1735,14 @@ FileDialog::FileDialog() {
|
|||
|
||||
dir_prev = memnew(Button);
|
||||
dir_prev->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
dir_prev->set_accessibility_name(ETR("Previous"));
|
||||
dir_prev->set_tooltip_text(ETR("Go to previous folder."));
|
||||
dir_next = memnew(Button);
|
||||
dir_next->set_accessibility_name(ETR("Next"));
|
||||
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_accessibility_name(ETR("Parent Folder"));
|
||||
dir_up->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
dir_up->set_tooltip_text(ETR("Go to parent folder."));
|
||||
hbc->add_child(dir_prev);
|
||||
|
|
@ -1738,22 +1752,27 @@ FileDialog::FileDialog() {
|
|||
dir_next->connect(SceneStringName(pressed), callable_mp(this, &FileDialog::_go_forward));
|
||||
dir_up->connect(SceneStringName(pressed), callable_mp(this, &FileDialog::_go_up));
|
||||
|
||||
hbc->add_child(memnew(Label(ETR("Path:"))));
|
||||
Label *lbl_path = memnew(Label(ETR("Path:")));
|
||||
lbl_path->set_focus_mode(Control::FOCUS_NONE);
|
||||
hbc->add_child(lbl_path);
|
||||
|
||||
drives_container = memnew(HBoxContainer);
|
||||
hbc->add_child(drives_container);
|
||||
|
||||
drives = memnew(OptionButton);
|
||||
drives->connect(SceneStringName(item_selected), callable_mp(this, &FileDialog::_select_drive));
|
||||
drives->set_accessibility_name(ETR("Drive"));
|
||||
hbc->add_child(drives);
|
||||
|
||||
dir = memnew(LineEdit);
|
||||
dir->set_accessibility_name(ETR("Directory Path"));
|
||||
dir->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
|
||||
hbc->add_child(dir);
|
||||
dir->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
||||
refresh = memnew(Button);
|
||||
refresh->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
refresh->set_accessibility_name(ETR("Refresh"));
|
||||
refresh->set_tooltip_text(ETR("Refresh files."));
|
||||
refresh->connect(SceneStringName(pressed), callable_mp(this, &FileDialog::update_file_list));
|
||||
hbc->add_child(refresh);
|
||||
|
|
@ -1762,6 +1781,7 @@ FileDialog::FileDialog() {
|
|||
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_accessibility_name(ETR("Show Hidden Files"));
|
||||
show_hidden->set_tooltip_text(ETR("Toggle the visibility of hidden files."));
|
||||
show_hidden->connect(SceneStringName(toggled), callable_mp(this, &FileDialog::set_show_hidden_files));
|
||||
hbc->add_child(show_hidden);
|
||||
|
|
@ -1770,7 +1790,8 @@ FileDialog::FileDialog() {
|
|||
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->set_accessibility_name(ETR("Filter File Names"));
|
||||
show_filename_filter_button->set_tooltip_text(ETR("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);
|
||||
|
||||
|
|
@ -1779,6 +1800,7 @@ FileDialog::FileDialog() {
|
|||
|
||||
makedir = memnew(Button);
|
||||
makedir->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
makedir->set_accessibility_name(ETR("Create New Folder"));
|
||||
makedir->set_tooltip_text(ETR("Create a new folder."));
|
||||
makedir->connect(SceneStringName(pressed), callable_mp(this, &FileDialog::_make_dir));
|
||||
hbc->add_child(makedir);
|
||||
|
|
@ -1786,6 +1808,7 @@ FileDialog::FileDialog() {
|
|||
|
||||
tree = memnew(Tree);
|
||||
tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
|
||||
tree->set_accessibility_name(ETR("Directories and Files"));
|
||||
tree->set_hide_root(true);
|
||||
vbox->add_margin_child(ETR("Directories & Files:"), tree, true);
|
||||
|
||||
|
|
@ -1797,19 +1820,24 @@ FileDialog::FileDialog() {
|
|||
tree->add_child(message);
|
||||
|
||||
filename_filter_box = memnew(HBoxContainer);
|
||||
filename_filter_box->add_child(memnew(Label(RTR("Filter:"))));
|
||||
filename_filter_box->add_child(memnew(Label(ETR("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->set_accessibility_name(ETR("Filename Filter"));
|
||||
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:"))));
|
||||
Label *lbl_file = memnew(Label(ETR("File:")));
|
||||
lbl_file->set_focus_mode(Control::FOCUS_NONE);
|
||||
file_box->add_child(lbl_file);
|
||||
|
||||
file = memnew(LineEdit);
|
||||
file->set_accessibility_name(ETR("File Name"));
|
||||
file->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
|
||||
file->set_stretch_ratio(4);
|
||||
file->set_h_size_flags(Control::SIZE_EXPAND_FILL);
|
||||
|
|
@ -1878,6 +1906,7 @@ FileDialog::FileDialog() {
|
|||
|
||||
set_hide_on_ok(false);
|
||||
set_size(Size2(640, 360));
|
||||
set_internal_ok_text(ETR("Save")); // Default mode text.
|
||||
|
||||
if (register_func) {
|
||||
register_func(this);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FILE_DIALOG_H
|
||||
#define FILE_DIALOG_H
|
||||
#pragma once
|
||||
|
||||
#include "box_container.h"
|
||||
#include "core/io/dir_access.h"
|
||||
|
|
@ -298,5 +297,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(FileDialog::FileMode);
|
||||
VARIANT_ENUM_CAST(FileDialog::Access);
|
||||
|
||||
#endif // FILE_DIALOG_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef FLOW_CONTAINER_H
|
||||
#define FLOW_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -116,5 +115,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(FlowContainer::AlignmentMode);
|
||||
VARIANT_ENUM_CAST(FlowContainer::LastWrapAlignmentMode);
|
||||
|
||||
#endif // FLOW_CONTAINER_H
|
||||
|
|
|
|||
652
engine/scene/gui/foldable_container.cpp
Normal file
652
engine/scene/gui/foldable_container.cpp
Normal file
|
|
@ -0,0 +1,652 @@
|
|||
/**************************************************************************/
|
||||
/* foldable_container.cpp */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "foldable_container.h"
|
||||
|
||||
#include "scene/resources/text_line.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
Size2 FoldableContainer::get_minimum_size() const {
|
||||
_update_title_min_size();
|
||||
|
||||
if (folded) {
|
||||
return title_minimum_size;
|
||||
}
|
||||
Size2 ms;
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Control *c = as_sortable_control(get_child(i));
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
ms = ms.max(c->get_combined_minimum_size());
|
||||
}
|
||||
ms += theme_cache.panel_style->get_minimum_size();
|
||||
|
||||
return Size2(MAX(ms.width, title_minimum_size.width), ms.height + title_minimum_size.height);
|
||||
}
|
||||
|
||||
void FoldableContainer::fold() {
|
||||
set_folded(true);
|
||||
emit_signal(SNAME("folding_changed"), folded);
|
||||
}
|
||||
|
||||
void FoldableContainer::expand() {
|
||||
set_folded(false);
|
||||
emit_signal(SNAME("folding_changed"), folded);
|
||||
}
|
||||
|
||||
void FoldableContainer::set_folded(bool p_folded) {
|
||||
if (folded != p_folded) {
|
||||
if (!changing_group && foldable_group.is_valid()) {
|
||||
if (!p_folded) {
|
||||
_update_group();
|
||||
foldable_group->emit_signal(SNAME("expanded"), this);
|
||||
} else if (!foldable_group->updating_group && foldable_group->get_expanded_container() == this && !foldable_group->is_allow_folding_all()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
folded = p_folded;
|
||||
|
||||
update_minimum_size();
|
||||
queue_sort();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
bool FoldableContainer::is_folded() const {
|
||||
return folded;
|
||||
}
|
||||
|
||||
void FoldableContainer::set_foldable_group(const Ref<FoldableGroup> &p_group) {
|
||||
if (foldable_group.is_valid()) {
|
||||
foldable_group->containers.erase(this);
|
||||
}
|
||||
|
||||
foldable_group = p_group;
|
||||
|
||||
if (foldable_group.is_valid()) {
|
||||
changing_group = true;
|
||||
if (folded && !foldable_group->get_expanded_container() && !foldable_group->is_allow_folding_all()) {
|
||||
set_folded(false);
|
||||
} else if (!folded && foldable_group->get_expanded_container()) {
|
||||
set_folded(true);
|
||||
}
|
||||
foldable_group->containers.insert(this);
|
||||
changing_group = false;
|
||||
}
|
||||
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
Ref<FoldableGroup> FoldableContainer::get_foldable_group() const {
|
||||
return foldable_group;
|
||||
}
|
||||
|
||||
void FoldableContainer::set_text(const String &p_text) {
|
||||
if (text == p_text) {
|
||||
return;
|
||||
}
|
||||
text = p_text;
|
||||
_shape();
|
||||
update_minimum_size();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
String FoldableContainer::get_text() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
void FoldableContainer::set_text_alignment(HorizontalAlignment p_alignment) {
|
||||
ERR_FAIL_INDEX((int)p_alignment, 3);
|
||||
text_alignment = p_alignment;
|
||||
|
||||
if (_get_actual_alignment() != text_buf->get_horizontal_alignment()) {
|
||||
_shape();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalAlignment FoldableContainer::get_text_alignment() const {
|
||||
return text_alignment;
|
||||
}
|
||||
|
||||
void FoldableContainer::set_language(const String &p_language) {
|
||||
if (language == p_language) {
|
||||
return;
|
||||
}
|
||||
language = p_language;
|
||||
_shape();
|
||||
update_minimum_size();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
String FoldableContainer::get_language() const {
|
||||
return language;
|
||||
}
|
||||
|
||||
void FoldableContainer::set_text_direction(TextDirection p_text_direction) {
|
||||
ERR_FAIL_INDEX(int(p_text_direction), 4);
|
||||
if (text_direction == p_text_direction) {
|
||||
return;
|
||||
}
|
||||
text_direction = p_text_direction;
|
||||
_shape();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
Control::TextDirection FoldableContainer::get_text_direction() const {
|
||||
return text_direction;
|
||||
}
|
||||
|
||||
void FoldableContainer::set_text_overrun_behavior(TextServer::OverrunBehavior p_overrun_behavior) {
|
||||
if (overrun_behavior == p_overrun_behavior) {
|
||||
return;
|
||||
}
|
||||
overrun_behavior = p_overrun_behavior;
|
||||
_shape();
|
||||
update_minimum_size();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
TextServer::OverrunBehavior FoldableContainer::get_text_overrun_behavior() const {
|
||||
return overrun_behavior;
|
||||
}
|
||||
|
||||
void FoldableContainer::set_title_position(TitlePosition p_title_position) {
|
||||
ERR_FAIL_INDEX(p_title_position, POSITION_MAX);
|
||||
if (title_position == p_title_position) {
|
||||
return;
|
||||
}
|
||||
title_position = p_title_position;
|
||||
queue_redraw();
|
||||
queue_sort();
|
||||
}
|
||||
|
||||
FoldableContainer::TitlePosition FoldableContainer::get_title_position() const {
|
||||
return title_position;
|
||||
}
|
||||
|
||||
void FoldableContainer::add_title_bar_control(Control *p_control) {
|
||||
ERR_FAIL_NULL(p_control);
|
||||
if (p_control->get_parent()) {
|
||||
p_control->get_parent()->remove_child(p_control);
|
||||
ERR_FAIL_COND_MSG(p_control->get_parent() != nullptr, "Failed to remove control from parent.");
|
||||
}
|
||||
add_child(p_control, false, INTERNAL_MODE_FRONT);
|
||||
title_controls.push_back(p_control);
|
||||
}
|
||||
|
||||
void FoldableContainer::remove_title_bar_control(Control *p_control) {
|
||||
ERR_FAIL_NULL(p_control);
|
||||
|
||||
int64_t index = title_controls.find(p_control);
|
||||
ERR_FAIL_COND_MSG(index == -1, "Can't remove control from title bar.");
|
||||
|
||||
title_controls.remove_at(index);
|
||||
remove_child(p_control);
|
||||
}
|
||||
|
||||
void FoldableContainer::gui_input(const Ref<InputEvent> &p_event) {
|
||||
ERR_FAIL_COND(p_event.is_null());
|
||||
|
||||
Ref<InputEventMouseMotion> m = p_event;
|
||||
if (m.is_valid()) {
|
||||
Rect2 title_rect = Rect2(0, (title_position == POSITION_TOP) ? 0 : get_size().height - title_minimum_size.height, get_size().width, title_minimum_size.height);
|
||||
if (title_rect.has_point(m->get_position())) {
|
||||
if (!is_hovering) {
|
||||
is_hovering = true;
|
||||
queue_redraw();
|
||||
}
|
||||
} else if (is_hovering) {
|
||||
is_hovering = false;
|
||||
queue_redraw();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_event->is_action_pressed(SNAME("ui_accept"), false, true)) {
|
||||
set_folded(!folded);
|
||||
emit_signal(SNAME("folding_changed"), folded);
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseButton> b = p_event;
|
||||
if (b.is_valid()) {
|
||||
Rect2 title_rect = Rect2(0, (title_position == POSITION_TOP) ? 0 : get_size().height - title_minimum_size.height, get_size().width, title_minimum_size.height);
|
||||
if (b->get_button_index() == MouseButton::LEFT && b->is_pressed() && title_rect.has_point(b->get_position())) {
|
||||
set_folded(!folded);
|
||||
emit_signal(SNAME("folding_changed"), folded);
|
||||
accept_event();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String FoldableContainer::get_tooltip(const Point2 &p_pos) const {
|
||||
if (Rect2(0, (title_position == POSITION_TOP) ? 0 : get_size().height - title_minimum_size.height, get_size().width, title_minimum_size.height).has_point(p_pos)) {
|
||||
return Control::get_tooltip(p_pos);
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void FoldableContainer::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_DRAW: {
|
||||
RID ci = get_canvas_item();
|
||||
Size2 size = get_size();
|
||||
int h_separation = _get_h_separation();
|
||||
|
||||
Ref<StyleBox> title_style = _get_title_style();
|
||||
Ref<Texture2D> icon = _get_title_icon();
|
||||
|
||||
real_t title_controls_width = _get_title_controls_width();
|
||||
if (title_controls_width > 0) {
|
||||
title_controls_width += h_separation;
|
||||
}
|
||||
|
||||
Rect2 title_rect(
|
||||
Point2(0, (title_position == POSITION_TOP) ? 0 : size.height - title_minimum_size.height),
|
||||
Size2(size.width, title_minimum_size.height));
|
||||
_draw_flippable_stylebox(title_style, title_rect);
|
||||
|
||||
Size2 title_ms = title_style->get_minimum_size();
|
||||
int title_text_width = size.width - title_ms.width;
|
||||
|
||||
int title_style_ofs = (title_position == POSITION_TOP) ? title_style->get_margin(SIDE_TOP) : title_style->get_margin(SIDE_BOTTOM);
|
||||
Point2 title_text_pos(title_style->get_margin(SIDE_LEFT), title_style_ofs);
|
||||
title_text_pos.y += MAX((title_minimum_size.height - title_ms.height - text_buf->get_size().height) * 0.5, 0);
|
||||
|
||||
title_text_width -= icon->get_width() + h_separation + title_controls_width;
|
||||
Point2 icon_pos(0, MAX((title_minimum_size.height - title_ms.height - icon->get_height()) * 0.5, 0) + title_style_ofs);
|
||||
|
||||
bool rtl = is_layout_rtl();
|
||||
if (rtl) {
|
||||
icon_pos.x = size.width - title_style->get_margin(SIDE_RIGHT) - icon->get_width();
|
||||
title_text_pos.x += title_controls_width;
|
||||
} else {
|
||||
icon_pos.x = title_style->get_margin(SIDE_LEFT);
|
||||
title_text_pos.x += icon->get_width() + h_separation;
|
||||
}
|
||||
icon->draw(ci, title_rect.position + icon_pos);
|
||||
|
||||
Color font_color = folded ? theme_cache.title_collapsed_font_color : theme_cache.title_font_color;
|
||||
if (is_hovering) {
|
||||
font_color = theme_cache.title_hovered_font_color;
|
||||
}
|
||||
text_buf->set_width(title_text_width);
|
||||
|
||||
if (title_text_width > 0) {
|
||||
if (theme_cache.title_font_outline_size > 0 && theme_cache.title_font_outline_color.a > 0) {
|
||||
text_buf->draw_outline(ci, title_rect.position + title_text_pos, theme_cache.title_font_outline_size, theme_cache.title_font_outline_color);
|
||||
}
|
||||
text_buf->draw(ci, title_rect.position + title_text_pos, font_color);
|
||||
}
|
||||
|
||||
if (!folded) {
|
||||
Rect2 panel_rect(
|
||||
Point2(0, (title_position == POSITION_TOP) ? title_minimum_size.height : 0),
|
||||
Size2(size.width, size.height - title_minimum_size.height));
|
||||
_draw_flippable_stylebox(theme_cache.panel_style, panel_rect);
|
||||
}
|
||||
|
||||
if (has_focus()) {
|
||||
Rect2 focus_rect = folded ? title_rect : Rect2(Point2(), size);
|
||||
_draw_flippable_stylebox(theme_cache.focus_style, focus_rect);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_SORT_CHILDREN: {
|
||||
bool rtl = is_layout_rtl();
|
||||
const Vector2 size = get_size();
|
||||
const Ref<StyleBox> title_style = _get_title_style();
|
||||
|
||||
uint32_t title_count = title_controls.size();
|
||||
if (title_count > 0) {
|
||||
int h_separation = MAX(theme_cache.h_separation, 0);
|
||||
real_t offset = 0.0;
|
||||
if (rtl) {
|
||||
offset = title_style->get_margin(SIDE_LEFT);
|
||||
} else {
|
||||
offset = _get_title_controls_width();
|
||||
offset = size.x - title_style->get_margin(SIDE_RIGHT) - offset;
|
||||
}
|
||||
|
||||
real_t v_center = title_minimum_size.y * 0.5;
|
||||
if (title_position == POSITION_BOTTOM) {
|
||||
v_center = size.y - v_center + (title_style->get_margin(SIDE_BOTTOM) - title_style->get_margin(SIDE_TOP)) * 0.5;
|
||||
} else {
|
||||
v_center += (title_style->get_margin(SIDE_TOP) - title_style->get_margin(SIDE_BOTTOM)) * 0.5;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < title_count; i++) {
|
||||
Control *control = title_controls[rtl ? title_count - i - 1 : i];
|
||||
if (!control->is_visible()) {
|
||||
continue;
|
||||
}
|
||||
Rect2 rect(Vector2(), control->get_combined_minimum_size());
|
||||
rect.position.x = offset;
|
||||
rect.position.y = v_center - rect.size.y * 0.5;
|
||||
fit_child_in_rect(control, rect);
|
||||
|
||||
offset += rect.size.x + h_separation;
|
||||
}
|
||||
}
|
||||
|
||||
Rect2 inner_rect;
|
||||
inner_rect.position.x = rtl ? theme_cache.panel_style->get_margin(SIDE_RIGHT) : theme_cache.panel_style->get_margin(SIDE_LEFT);
|
||||
inner_rect.size.x = size.x - theme_cache.panel_style->get_margin(SIDE_LEFT) - theme_cache.panel_style->get_margin(SIDE_RIGHT);
|
||||
inner_rect.position.y = theme_cache.panel_style->get_margin(SIDE_TOP);
|
||||
|
||||
inner_rect.size.y = size.y - theme_cache.panel_style->get_margin(SIDE_TOP) - theme_cache.panel_style->get_margin(SIDE_BOTTOM) - title_minimum_size.y;
|
||||
if (title_position == POSITION_TOP) {
|
||||
inner_rect.position.y += title_minimum_size.y;
|
||||
}
|
||||
|
||||
for (int i = 0; i < get_child_count(false); i++) {
|
||||
Control *c = as_sortable_control(get_child(i, false), SortableVisibilityMode::IGNORE);
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
c->set_visible(!folded);
|
||||
|
||||
if (!folded) {
|
||||
fit_child_in_rect(c, inner_rect);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_MOUSE_EXIT: {
|
||||
if (is_hovering) {
|
||||
is_hovering = false;
|
||||
queue_redraw();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
case NOTIFICATION_TRANSLATION_CHANGED:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
_shape();
|
||||
update_minimum_size();
|
||||
queue_redraw();
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
real_t FoldableContainer::_get_title_controls_width() const {
|
||||
real_t width = 0.0;
|
||||
int visible_controls = 0;
|
||||
for (const Control *control : title_controls) {
|
||||
if (control->is_visible()) {
|
||||
width += control->get_combined_minimum_size().x;
|
||||
visible_controls++;
|
||||
}
|
||||
}
|
||||
if (visible_controls > 1) {
|
||||
width += _get_h_separation() * (visible_controls - 1);
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
Ref<StyleBox> FoldableContainer::_get_title_style() const {
|
||||
if (is_hovering) {
|
||||
return folded ? theme_cache.title_collapsed_hover_style : theme_cache.title_hover_style;
|
||||
}
|
||||
return folded ? theme_cache.title_collapsed_style : theme_cache.title_style;
|
||||
}
|
||||
|
||||
Ref<Texture2D> FoldableContainer::_get_title_icon() const {
|
||||
if (!folded) {
|
||||
return (title_position == POSITION_TOP) ? theme_cache.expanded_arrow : theme_cache.expanded_arrow_mirrored;
|
||||
} else if (is_layout_rtl()) {
|
||||
return theme_cache.folded_arrow_mirrored;
|
||||
}
|
||||
return theme_cache.folded_arrow;
|
||||
}
|
||||
|
||||
void FoldableContainer::_update_title_min_size() const {
|
||||
Ref<StyleBox> title_style = folded ? theme_cache.title_collapsed_style : theme_cache.title_style;
|
||||
Ref<Texture2D> icon = _get_title_icon();
|
||||
Size2 title_ms = title_style->get_minimum_size();
|
||||
int h_separation = _get_h_separation();
|
||||
|
||||
title_minimum_size = title_ms;
|
||||
title_minimum_size.width += icon->get_width();
|
||||
|
||||
if (!text.is_empty()) {
|
||||
title_minimum_size.width += h_separation;
|
||||
Size2 text_size = text_buf->get_size();
|
||||
title_minimum_size.height += MAX(text_size.height, icon->get_height());
|
||||
if (overrun_behavior == TextServer::OverrunBehavior::OVERRUN_NO_TRIMMING) {
|
||||
title_minimum_size.width += text_size.width;
|
||||
}
|
||||
} else {
|
||||
title_minimum_size.height += icon->get_height();
|
||||
}
|
||||
|
||||
if (!title_controls.is_empty()) {
|
||||
real_t controls_height = 0;
|
||||
int visible_controls = 0;
|
||||
|
||||
for (const Control *control : title_controls) {
|
||||
if (!control->is_visible()) {
|
||||
continue;
|
||||
}
|
||||
Vector2 size = control->get_combined_minimum_size();
|
||||
title_minimum_size.width += size.width;
|
||||
controls_height = MAX(controls_height, size.height);
|
||||
visible_controls++;
|
||||
}
|
||||
if (visible_controls > 0) {
|
||||
title_minimum_size.width += h_separation * visible_controls;
|
||||
}
|
||||
title_minimum_size.height = MAX(title_minimum_size.height, title_ms.height + controls_height);
|
||||
}
|
||||
}
|
||||
|
||||
void FoldableContainer::_shape() {
|
||||
Ref<Font> font = theme_cache.title_font;
|
||||
int font_size = theme_cache.title_font_size;
|
||||
if (font.is_null() || font_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
text_buf->clear();
|
||||
text_buf->set_width(-1);
|
||||
|
||||
if (text_direction == TEXT_DIRECTION_INHERITED) {
|
||||
text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
|
||||
} else {
|
||||
text_buf->set_direction((TextServer::Direction)text_direction);
|
||||
}
|
||||
text_buf->set_horizontal_alignment(_get_actual_alignment());
|
||||
text_buf->set_text_overrun_behavior(overrun_behavior);
|
||||
text_buf->add_string(atr(text), font, font_size, language);
|
||||
}
|
||||
|
||||
HorizontalAlignment FoldableContainer::_get_actual_alignment() const {
|
||||
if (is_layout_rtl()) {
|
||||
if (text_alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
|
||||
return HORIZONTAL_ALIGNMENT_LEFT;
|
||||
} else if (text_alignment == HORIZONTAL_ALIGNMENT_LEFT) {
|
||||
return HORIZONTAL_ALIGNMENT_RIGHT;
|
||||
}
|
||||
}
|
||||
return text_alignment;
|
||||
}
|
||||
|
||||
void FoldableContainer::_update_group() {
|
||||
foldable_group->updating_group = true;
|
||||
for (FoldableContainer *container : foldable_group->containers) {
|
||||
if (container != this) {
|
||||
container->set_folded(true);
|
||||
}
|
||||
}
|
||||
foldable_group->updating_group = false;
|
||||
}
|
||||
|
||||
void FoldableContainer::_draw_flippable_stylebox(const Ref<StyleBox> p_stylebox, const Rect2 &p_rect) {
|
||||
if (title_position == POSITION_BOTTOM) {
|
||||
Rect2 rect(-p_rect.position, p_rect.size);
|
||||
draw_set_transform(Point2(0.0, p_stylebox->get_draw_rect(rect).size.height), 0.0, Size2(1.0, -1.0));
|
||||
p_stylebox->draw(get_canvas_item(), rect);
|
||||
draw_set_transform_matrix(Transform2D());
|
||||
} else {
|
||||
p_stylebox->draw(get_canvas_item(), p_rect);
|
||||
}
|
||||
}
|
||||
|
||||
void FoldableContainer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("fold"), &FoldableContainer::fold);
|
||||
ClassDB::bind_method(D_METHOD("expand"), &FoldableContainer::expand);
|
||||
ClassDB::bind_method(D_METHOD("set_folded", "folded"), &FoldableContainer::set_folded);
|
||||
ClassDB::bind_method(D_METHOD("is_folded"), &FoldableContainer::is_folded);
|
||||
ClassDB::bind_method(D_METHOD("set_foldable_group", "button_group"), &FoldableContainer::set_foldable_group);
|
||||
ClassDB::bind_method(D_METHOD("get_foldable_group"), &FoldableContainer::get_foldable_group);
|
||||
ClassDB::bind_method(D_METHOD("set_text", "text"), &FoldableContainer::set_text);
|
||||
ClassDB::bind_method(D_METHOD("get_text"), &FoldableContainer::get_text);
|
||||
ClassDB::bind_method(D_METHOD("set_title_alignment", "alignment"), &FoldableContainer::set_text_alignment);
|
||||
ClassDB::bind_method(D_METHOD("get_title_alignment"), &FoldableContainer::get_text_alignment);
|
||||
ClassDB::bind_method(D_METHOD("set_language", "language"), &FoldableContainer::set_language);
|
||||
ClassDB::bind_method(D_METHOD("get_language"), &FoldableContainer::get_language);
|
||||
ClassDB::bind_method(D_METHOD("set_text_direction", "text_direction"), &FoldableContainer::set_text_direction);
|
||||
ClassDB::bind_method(D_METHOD("get_text_direction"), &FoldableContainer::get_text_direction);
|
||||
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &FoldableContainer::set_text_overrun_behavior);
|
||||
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &FoldableContainer::get_text_overrun_behavior);
|
||||
ClassDB::bind_method(D_METHOD("set_title_position", "title_position"), &FoldableContainer::set_title_position);
|
||||
ClassDB::bind_method(D_METHOD("get_title_position"), &FoldableContainer::get_title_position);
|
||||
ClassDB::bind_method(D_METHOD("add_title_bar_control", "control"), &FoldableContainer::add_title_bar_control);
|
||||
ClassDB::bind_method(D_METHOD("remove_title_bar_control", "control"), &FoldableContainer::remove_title_bar_control);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("folding_changed", PropertyInfo(Variant::BOOL, "is_folded")));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "folded"), "set_folded", "is_folded");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "title_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_title_alignment", "get_title_alignment");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "title_position", PROPERTY_HINT_ENUM, "Top,Bottom"), "set_title_position", "get_title_position");
|
||||
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::OBJECT, "foldable_group", PROPERTY_HINT_RESOURCE_TYPE, "FoldableGroup"), "set_foldable_group", "get_foldable_group");
|
||||
|
||||
ADD_GROUP("BiDi", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID), "set_language", "get_language");
|
||||
|
||||
BIND_ENUM_CONSTANT(POSITION_TOP);
|
||||
BIND_ENUM_CONSTANT(POSITION_BOTTOM);
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, FoldableContainer, title_style, "title_panel");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, FoldableContainer, title_hover_style, "title_hover_panel");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, FoldableContainer, title_collapsed_style, "title_collapsed_panel");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, FoldableContainer, title_collapsed_hover_style, "title_collapsed_hover_panel");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, FoldableContainer, focus_style, "focus");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, FoldableContainer, panel_style, "panel");
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT, FoldableContainer, title_font, "font");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_FONT_SIZE, FoldableContainer, title_font_size, "font_size");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_CONSTANT, FoldableContainer, title_font_outline_size, "outline_size");
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, FoldableContainer, title_font_color, "font_color");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, FoldableContainer, title_hovered_font_color, "hover_font_color");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, FoldableContainer, title_collapsed_font_color, "collapsed_font_color");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_COLOR, FoldableContainer, title_font_outline_color, "font_outline_color");
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FoldableContainer, expanded_arrow);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FoldableContainer, expanded_arrow_mirrored);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FoldableContainer, folded_arrow);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, FoldableContainer, folded_arrow_mirrored);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, FoldableContainer, h_separation);
|
||||
}
|
||||
|
||||
FoldableContainer::FoldableContainer(const String &p_text) {
|
||||
text_buf.instantiate();
|
||||
set_text(p_text);
|
||||
set_focus_mode(FOCUS_ALL);
|
||||
set_mouse_filter(MOUSE_FILTER_STOP);
|
||||
}
|
||||
|
||||
FoldableContainer::~FoldableContainer() {
|
||||
if (foldable_group.is_valid()) {
|
||||
foldable_group->containers.erase(this);
|
||||
}
|
||||
}
|
||||
|
||||
FoldableContainer *FoldableGroup::get_expanded_container() const {
|
||||
for (FoldableContainer *container : containers) {
|
||||
if (!container->is_folded()) {
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FoldableGroup::set_allow_folding_all(bool p_enabled) {
|
||||
allow_folding_all = p_enabled;
|
||||
if (!allow_folding_all && !get_expanded_container() && containers.size() > 0) {
|
||||
updating_group = true;
|
||||
(*containers.begin())->set_folded(false);
|
||||
updating_group = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FoldableGroup::is_allow_folding_all() const {
|
||||
return allow_folding_all;
|
||||
}
|
||||
|
||||
void FoldableGroup::get_containers(List<FoldableContainer *> *r_containers) const {
|
||||
for (FoldableContainer *container : containers) {
|
||||
r_containers->push_back(container);
|
||||
}
|
||||
}
|
||||
|
||||
TypedArray<FoldableContainer> FoldableGroup::_get_containers() const {
|
||||
TypedArray<FoldableContainer> foldable_containers;
|
||||
for (const FoldableContainer *container : containers) {
|
||||
foldable_containers.push_back(container);
|
||||
}
|
||||
|
||||
return foldable_containers;
|
||||
}
|
||||
|
||||
void FoldableGroup::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_expanded_container"), &FoldableGroup::get_expanded_container);
|
||||
ClassDB::bind_method(D_METHOD("get_containers"), &FoldableGroup::_get_containers);
|
||||
ClassDB::bind_method(D_METHOD("set_allow_folding_all", "enabled"), &FoldableGroup::set_allow_folding_all);
|
||||
ClassDB::bind_method(D_METHOD("is_allow_folding_all"), &FoldableGroup::is_allow_folding_all);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_folding_all"), "set_allow_folding_all", "is_allow_folding_all");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("expanded", PropertyInfo(Variant::OBJECT, "container", PROPERTY_HINT_RESOURCE_TYPE, "FoldableContainer")));
|
||||
}
|
||||
|
||||
FoldableGroup::FoldableGroup() {
|
||||
set_local_to_scene(true);
|
||||
}
|
||||
171
engine/scene/gui/foldable_container.h
Normal file
171
engine/scene/gui/foldable_container.h
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/**************************************************************************/
|
||||
/* foldable_container.h */
|
||||
/**************************************************************************/
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
class FoldableGroup;
|
||||
class TextLine;
|
||||
|
||||
class FoldableContainer : public Container {
|
||||
GDCLASS(FoldableContainer, Container);
|
||||
|
||||
public:
|
||||
enum TitlePosition {
|
||||
POSITION_TOP,
|
||||
POSITION_BOTTOM,
|
||||
POSITION_MAX
|
||||
};
|
||||
|
||||
private:
|
||||
bool folded = false;
|
||||
String text;
|
||||
Ref<FoldableGroup> foldable_group;
|
||||
String language;
|
||||
TextDirection text_direction = TEXT_DIRECTION_AUTO;
|
||||
HorizontalAlignment text_alignment = HORIZONTAL_ALIGNMENT_LEFT;
|
||||
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
|
||||
TitlePosition title_position = POSITION_TOP;
|
||||
|
||||
Ref<TextLine> text_buf;
|
||||
bool changing_group = false;
|
||||
bool is_hovering = false;
|
||||
mutable Vector2 title_minimum_size;
|
||||
|
||||
LocalVector<Control *> title_controls;
|
||||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> title_style;
|
||||
Ref<StyleBox> title_hover_style;
|
||||
Ref<StyleBox> title_collapsed_style;
|
||||
Ref<StyleBox> title_collapsed_hover_style;
|
||||
Ref<StyleBox> panel_style;
|
||||
Ref<StyleBox> focus_style;
|
||||
|
||||
Color title_font_color;
|
||||
Color title_hovered_font_color;
|
||||
Color title_collapsed_font_color;
|
||||
Color title_font_outline_color;
|
||||
|
||||
Ref<Font> title_font;
|
||||
int title_font_size = 0;
|
||||
int title_font_outline_size = 0;
|
||||
|
||||
Ref<Texture2D> expanded_arrow;
|
||||
Ref<Texture2D> expanded_arrow_mirrored;
|
||||
Ref<Texture2D> folded_arrow;
|
||||
Ref<Texture2D> folded_arrow_mirrored;
|
||||
|
||||
int h_separation = 0;
|
||||
} theme_cache;
|
||||
|
||||
Ref<StyleBox> _get_title_style() const;
|
||||
Ref<Texture2D> _get_title_icon() const;
|
||||
int _get_h_separation() const { return MAX(theme_cache.h_separation, 0); }
|
||||
real_t _get_title_controls_width() const;
|
||||
|
||||
void _update_title_min_size() const;
|
||||
void _shape();
|
||||
HorizontalAlignment _get_actual_alignment() const;
|
||||
void _update_group();
|
||||
void _draw_flippable_stylebox(const Ref<StyleBox> p_stylebox, const Rect2 &p_rect);
|
||||
|
||||
protected:
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
virtual String get_tooltip(const Point2 &p_pos) const override;
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void fold();
|
||||
void expand();
|
||||
|
||||
void set_folded(bool p_folded);
|
||||
bool is_folded() const;
|
||||
|
||||
void set_foldable_group(const Ref<FoldableGroup> &p_group);
|
||||
Ref<FoldableGroup> get_foldable_group() const;
|
||||
|
||||
void set_text(const String &p_text);
|
||||
String get_text() const;
|
||||
|
||||
void set_text_alignment(HorizontalAlignment p_alignment);
|
||||
HorizontalAlignment get_text_alignment() const;
|
||||
|
||||
void set_text_direction(TextDirection p_text_direction);
|
||||
TextDirection get_text_direction() const;
|
||||
|
||||
void set_text_overrun_behavior(TextServer::OverrunBehavior p_overrun_behavior);
|
||||
TextServer::OverrunBehavior get_text_overrun_behavior() const;
|
||||
|
||||
void set_language(const String &p_language);
|
||||
String get_language() const;
|
||||
|
||||
void set_title_position(TitlePosition p_title_position);
|
||||
TitlePosition get_title_position() const;
|
||||
|
||||
void add_title_bar_control(Control *p_control);
|
||||
void remove_title_bar_control(Control *p_control);
|
||||
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
virtual Vector<int> get_allowed_size_flags_horizontal() const override { return { SIZE_FILL, SIZE_SHRINK_BEGIN, SIZE_SHRINK_CENTER, SIZE_SHRINK_END }; }
|
||||
virtual Vector<int> get_allowed_size_flags_vertical() const override { return { SIZE_FILL, SIZE_SHRINK_BEGIN, SIZE_SHRINK_CENTER, SIZE_SHRINK_END }; }
|
||||
|
||||
FoldableContainer(const String &p_text = String());
|
||||
~FoldableContainer();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(FoldableContainer::TitlePosition);
|
||||
|
||||
class FoldableGroup : public Resource {
|
||||
GDCLASS(FoldableGroup, Resource);
|
||||
|
||||
friend class FoldableContainer;
|
||||
|
||||
HashSet<FoldableContainer *> containers;
|
||||
bool allow_folding_all = false;
|
||||
bool updating_group = false;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
FoldableContainer *get_expanded_container() const;
|
||||
|
||||
void get_containers(List<FoldableContainer *> *r_containers) const;
|
||||
TypedArray<FoldableContainer> _get_containers() const;
|
||||
|
||||
void set_allow_folding_all(bool p_enabled);
|
||||
bool is_allow_folding_all() const;
|
||||
|
||||
FoldableGroup();
|
||||
};
|
||||
|
|
@ -353,6 +353,62 @@ int GraphEdit::get_connection_count(const StringName &p_node, int p_port) {
|
|||
return count;
|
||||
}
|
||||
|
||||
GraphNode *GraphEdit::get_input_connection_target(const StringName &p_node, int p_port) {
|
||||
for (const Ref<Connection> &conn : connections) {
|
||||
if (conn->to_node == p_node && conn->to_port == p_port) {
|
||||
GraphNode *from = Object::cast_to<GraphNode>(get_node(NodePath(conn->from_node)));
|
||||
if (from) {
|
||||
return from;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GraphNode *GraphEdit::get_output_connection_target(const StringName &p_node, int p_port) {
|
||||
for (const Ref<Connection> &conn : connections) {
|
||||
if (conn->from_node == p_node && conn->from_port == p_port) {
|
||||
GraphNode *to = Object::cast_to<GraphNode>(get_node(NodePath(conn->to_node)));
|
||||
if (to) {
|
||||
return to;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
String GraphEdit::get_connections_description(const StringName &p_node, int p_port) {
|
||||
String out;
|
||||
for (const Ref<Connection> &conn : connections) {
|
||||
if (conn->from_node == p_node && conn->from_port == p_port) {
|
||||
GraphNode *to = Object::cast_to<GraphNode>(get_node(NodePath(conn->to_node)));
|
||||
if (to) {
|
||||
if (!out.is_empty()) {
|
||||
out += ", ";
|
||||
}
|
||||
String name = to->get_accessibility_name();
|
||||
if (name.is_empty()) {
|
||||
name = to->get_name();
|
||||
}
|
||||
out += vformat(ETR("connection to %s (%s) port %d"), name, to->get_title(), conn->to_port);
|
||||
}
|
||||
} else if (conn->to_node == p_node && conn->to_port == p_port) {
|
||||
GraphNode *from = Object::cast_to<GraphNode>(get_node(NodePath(conn->from_node)));
|
||||
if (from) {
|
||||
if (!out.is_empty()) {
|
||||
out += ", ";
|
||||
}
|
||||
String name = from->get_accessibility_name();
|
||||
if (name.is_empty()) {
|
||||
name = from->get_name();
|
||||
}
|
||||
out += vformat(ETR("connection from %s (%s) port %d"), name, from->get_title(), conn->from_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void GraphEdit::set_scroll_offset(const Vector2 &p_offset) {
|
||||
setting_scroll_offset = true;
|
||||
h_scrollbar->set_value(p_offset.x);
|
||||
|
|
@ -780,6 +836,10 @@ void GraphEdit::_notification(int p_what) {
|
|||
// Draw background fill.
|
||||
draw_style_box(theme_cache.panel, Rect2(Point2(), get_size()));
|
||||
|
||||
if (has_focus()) {
|
||||
draw_style_box(theme_cache.panel_focus, Rect2(Point2(), get_size()));
|
||||
}
|
||||
|
||||
// Draw background grid.
|
||||
if (show_grid) {
|
||||
_draw_grid();
|
||||
|
|
@ -958,9 +1018,172 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void GraphEdit::start_keyboard_connecting(GraphNode *p_node, int p_in_port, int p_out_port) {
|
||||
if (!p_node || p_in_port == p_out_port || (p_in_port != -1 && p_out_port != -1)) {
|
||||
return;
|
||||
}
|
||||
connecting_valid = false;
|
||||
keyboard_connecting = true;
|
||||
if (p_in_port != -1) {
|
||||
Vector2 pos = p_node->get_input_port_position(p_in_port) * zoom + p_node->get_position();
|
||||
|
||||
if (right_disconnects || valid_right_disconnect_types.has(p_node->get_input_port_type(p_in_port))) {
|
||||
// Check disconnect.
|
||||
for (const Ref<Connection> &conn : connection_map[p_node->get_name()]) {
|
||||
if (conn->to_node == p_node->get_name() && conn->to_port == p_in_port) {
|
||||
Node *fr = get_node(NodePath(conn->from_node));
|
||||
if (Object::cast_to<GraphNode>(fr)) {
|
||||
connecting_from_node = conn->from_node;
|
||||
connecting_from_port_index = conn->from_port;
|
||||
connecting_from_output = true;
|
||||
connecting_type = Object::cast_to<GraphNode>(fr)->get_output_port_type(conn->from_port);
|
||||
connecting_color = Object::cast_to<GraphNode>(fr)->get_output_port_color(conn->from_port);
|
||||
connecting_target_valid = false;
|
||||
connecting_to_point = pos;
|
||||
just_disconnected = true;
|
||||
|
||||
if (connecting_type >= 0) {
|
||||
emit_signal(SNAME("disconnection_request"), conn->from_node, conn->from_port, conn->to_node, conn->to_port);
|
||||
fr = get_node(NodePath(connecting_from_node));
|
||||
if (Object::cast_to<GraphNode>(fr)) {
|
||||
connecting = true;
|
||||
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connecting_from_node = p_node->get_name();
|
||||
connecting_from_port_index = p_in_port;
|
||||
connecting_from_output = false;
|
||||
connecting_type = p_node->get_input_port_type(p_in_port);
|
||||
connecting_color = p_node->get_input_port_color(p_in_port);
|
||||
connecting_target_valid = false;
|
||||
connecting_to_point = pos;
|
||||
if (connecting_type >= 0) {
|
||||
connecting = true;
|
||||
just_disconnected = false;
|
||||
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (p_out_port != -1) {
|
||||
Vector2 pos = p_node->get_output_port_position(p_out_port) * zoom + p_node->get_position();
|
||||
|
||||
if (valid_left_disconnect_types.has(p_node->get_output_port_type(p_out_port))) {
|
||||
// Check disconnect.
|
||||
for (const Ref<Connection> &conn : connection_map[p_node->get_name()]) {
|
||||
if (conn->from_node == p_node->get_name() && conn->from_port == p_out_port) {
|
||||
Node *to = get_node(NodePath(conn->to_node));
|
||||
if (Object::cast_to<GraphNode>(to)) {
|
||||
connecting_from_node = conn->to_node;
|
||||
connecting_from_port_index = conn->to_port;
|
||||
connecting_from_output = false;
|
||||
connecting_type = Object::cast_to<GraphNode>(to)->get_input_port_type(conn->to_port);
|
||||
connecting_color = Object::cast_to<GraphNode>(to)->get_input_port_color(conn->to_port);
|
||||
connecting_target_valid = false;
|
||||
connecting_to_point = pos;
|
||||
|
||||
if (connecting_type >= 0) {
|
||||
just_disconnected = true;
|
||||
|
||||
emit_signal(SNAME("disconnection_request"), conn->from_node, conn->from_port, conn->to_node, conn->to_port);
|
||||
to = get_node(NodePath(connecting_from_node)); // Maybe it was erased.
|
||||
if (Object::cast_to<GraphNode>(to)) {
|
||||
connecting = true;
|
||||
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, false);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connecting_from_node = p_node->get_name();
|
||||
connecting_from_port_index = p_out_port;
|
||||
connecting_from_output = true;
|
||||
connecting_type = p_node->get_output_port_type(p_out_port);
|
||||
connecting_color = p_node->get_output_port_color(p_out_port);
|
||||
connecting_target_valid = false;
|
||||
connecting_to_point = pos;
|
||||
if (connecting_type >= 0) {
|
||||
connecting = true;
|
||||
just_disconnected = false;
|
||||
emit_signal(SNAME("connection_drag_started"), connecting_from_node, connecting_from_port_index, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEdit::end_keyboard_connecting(GraphNode *p_node, int p_in_port, int p_out_port) {
|
||||
if (!p_node) {
|
||||
return;
|
||||
}
|
||||
connecting_valid = true;
|
||||
connecting_target_valid = false;
|
||||
if (p_in_port != -1) {
|
||||
Vector2 pos = p_node->get_input_port_position(p_in_port) * zoom + p_node->get_position();
|
||||
|
||||
int type = p_node->get_input_port_type(p_in_port);
|
||||
if (type == connecting_type || p_node->is_ignoring_valid_connection_type() || valid_connection_types.has(ConnectionType(connecting_type, type))) {
|
||||
connecting_target_valid = true;
|
||||
connecting_to_point = pos;
|
||||
connecting_target_node = p_node->get_name();
|
||||
connecting_target_port_index = p_in_port;
|
||||
}
|
||||
}
|
||||
if (p_out_port != -1) {
|
||||
Vector2 pos = p_node->get_output_port_position(p_out_port) * zoom + p_node->get_position();
|
||||
|
||||
int type = p_node->get_output_port_type(p_out_port);
|
||||
if (type == connecting_type || p_node->is_ignoring_valid_connection_type() || valid_connection_types.has(ConnectionType(type, connecting_type))) {
|
||||
connecting_target_valid = true;
|
||||
connecting_to_point = pos;
|
||||
connecting_target_node = p_node->get_name();
|
||||
connecting_target_port_index = p_out_port;
|
||||
}
|
||||
}
|
||||
if (connecting_valid) {
|
||||
if (connecting && connecting_target_valid) {
|
||||
if (connecting_from_output) {
|
||||
emit_signal(SNAME("connection_request"), connecting_from_node, connecting_from_port_index, connecting_target_node, connecting_target_port_index);
|
||||
} else {
|
||||
emit_signal(SNAME("connection_request"), connecting_target_node, connecting_target_port_index, connecting_from_node, connecting_from_port_index);
|
||||
}
|
||||
} else if (!just_disconnected) {
|
||||
if (connecting_from_output) {
|
||||
emit_signal(SNAME("connection_to_empty"), connecting_from_node, connecting_from_port_index, Vector2());
|
||||
} else {
|
||||
emit_signal(SNAME("connection_from_empty"), connecting_from_node, connecting_from_port_index, Vector2());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyboard_connecting = false;
|
||||
if (connecting) {
|
||||
force_connection_drag_end();
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary GraphEdit::get_type_names() const {
|
||||
return type_names;
|
||||
}
|
||||
|
||||
void GraphEdit::set_type_names(const Dictionary &p_names) {
|
||||
type_names = p_names;
|
||||
}
|
||||
|
||||
void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) {
|
||||
Ref<InputEventMouseButton> mb = p_ev;
|
||||
if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
|
||||
if (keyboard_connecting) {
|
||||
force_connection_drag_end();
|
||||
keyboard_connecting = false;
|
||||
}
|
||||
connecting_valid = false;
|
||||
click_pos = mb->get_position() / zoom;
|
||||
for (int i = get_child_count() - 1; i >= 0; i--) {
|
||||
|
|
@ -1086,7 +1309,7 @@ void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) {
|
|||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_ev;
|
||||
if (mm.is_valid() && connecting) {
|
||||
if (mm.is_valid() && connecting && !keyboard_connecting) {
|
||||
connecting_to_point = mm->get_position();
|
||||
minimap->queue_redraw();
|
||||
callable_mp(this, &GraphEdit::_update_top_connection_layer).call_deferred();
|
||||
|
|
@ -1198,7 +1421,7 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &mpos
|
|||
control_rect.size *= zoom;
|
||||
control_rect.position += p_offset;
|
||||
|
||||
if (!control_rect.has_point(mpos) || p_control->get_mouse_filter() == MOUSE_FILTER_IGNORE) {
|
||||
if (!control_rect.has_point(mpos) || p_control->get_mouse_filter_with_recursive() == MOUSE_FILTER_IGNORE) {
|
||||
// Test children.
|
||||
for (int i = 0; i < p_control->get_child_count(); i++) {
|
||||
Control *child_rect = Object::cast_to<Control>(p_control->get_child(i));
|
||||
|
|
@ -1316,7 +1539,7 @@ Ref<GraphEdit::Connection> GraphEdit::get_closest_connection_at_point(const Vect
|
|||
|
||||
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]);
|
||||
const real_t distance = Geometry2D::get_distance_to_segment(transformed_point, points[i], points[i + 1]);
|
||||
if (distance <= lines_thickness * 0.5 + p_max_distance && distance < closest_distance) {
|
||||
closest_connection = conn;
|
||||
closest_distance = distance;
|
||||
|
|
@ -1663,7 +1886,7 @@ void GraphEdit::_draw_grid() {
|
|||
for (int i = from_pos.x; i < from_pos.x + len.x; i++) {
|
||||
Color color;
|
||||
|
||||
if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
|
||||
if (Math::abs(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
|
||||
color = theme_cache.grid_major;
|
||||
} else {
|
||||
color = theme_cache.grid_minor;
|
||||
|
|
@ -1676,7 +1899,7 @@ void GraphEdit::_draw_grid() {
|
|||
for (int i = from_pos.y; i < from_pos.y + len.y; i++) {
|
||||
Color color;
|
||||
|
||||
if (ABS(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
|
||||
if (Math::abs(i) % GRID_MINOR_STEPS_PER_MAJOR_LINE == 0) {
|
||||
color = theme_cache.grid_major;
|
||||
} else {
|
||||
color = theme_cache.grid_minor;
|
||||
|
|
@ -1694,7 +1917,7 @@ void GraphEdit::_draw_grid() {
|
|||
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) {
|
||||
if (Math::abs(i) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0 && Math::abs(j) % GRID_MINOR_STEPS_PER_MAJOR_DOT == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -2142,6 +2365,7 @@ void GraphEdit::force_connection_drag_end() {
|
|||
|
||||
connecting = false;
|
||||
connecting_valid = false;
|
||||
keyboard_connecting = false;
|
||||
minimap->queue_redraw();
|
||||
queue_redraw();
|
||||
connections_layer->queue_redraw();
|
||||
|
|
@ -2329,6 +2553,24 @@ TypedArray<Dictionary> GraphEdit::_get_connections_intersecting_with_rect(const
|
|||
return arr;
|
||||
}
|
||||
|
||||
TypedArray<Dictionary> GraphEdit::_get_connection_list_from_node(const StringName &p_node) const {
|
||||
ERR_FAIL_COND_V(!connection_map.has(p_node), TypedArray<Dictionary>());
|
||||
|
||||
List<Ref<GraphEdit::Connection>> connections_from_node = connection_map.get(p_node);
|
||||
TypedArray<Dictionary> connections_from_node_dict;
|
||||
|
||||
for (const Ref<Connection> &conn : connections_from_node) {
|
||||
Dictionary d;
|
||||
d["from_node"] = conn->from_node;
|
||||
d["from_port"] = conn->from_port;
|
||||
d["to_node"] = conn->to_node;
|
||||
d["to_port"] = conn->to_port;
|
||||
d["keep_alive"] = conn->keep_alive;
|
||||
connections_from_node_dict.push_back(d);
|
||||
}
|
||||
return connections_from_node_dict;
|
||||
}
|
||||
|
||||
void GraphEdit::_zoom_minus() {
|
||||
set_zoom(zoom / zoom_step);
|
||||
}
|
||||
|
|
@ -2689,6 +2931,7 @@ void GraphEdit::_bind_methods() {
|
|||
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_connection_list_from_node", "node"), &GraphEdit::_get_connection_list_from_node);
|
||||
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);
|
||||
ClassDB::bind_method(D_METHOD("force_connection_drag_end"), &GraphEdit::force_connection_drag_end);
|
||||
|
|
@ -2774,6 +3017,9 @@ void GraphEdit::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects);
|
||||
ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_type_names", "type_names"), &GraphEdit::set_type_names);
|
||||
ClassDB::bind_method(D_METHOD("get_type_names"), &GraphEdit::get_type_names);
|
||||
|
||||
GDVIRTUAL_BIND(_is_in_input_hotzone, "in_node", "in_port", "mouse_position");
|
||||
GDVIRTUAL_BIND(_is_in_output_hotzone, "in_node", "in_port", "mouse_position");
|
||||
|
||||
|
|
@ -2794,6 +3040,8 @@ void GraphEdit::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::INT, "panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans"), "set_panning_scheme", "get_panning_scheme");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "type_names", PROPERTY_HINT_DICTIONARY_TYPE, "int;String"), "set_type_names", "get_type_names");
|
||||
|
||||
ADD_GROUP("Connection Lines", "connection_lines");
|
||||
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");
|
||||
|
|
@ -2850,6 +3098,7 @@ void GraphEdit::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(GRID_PATTERN_DOTS);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEdit, panel);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEdit, panel_focus);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, grid_major);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, grid_minor);
|
||||
|
||||
|
|
@ -2958,7 +3207,8 @@ GraphEdit::GraphEdit() {
|
|||
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);
|
||||
zoom_minus_button->set_accessibility_name(ETR("Zoom Out"));
|
||||
zoom_minus_button->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
menu_hbox->add_child(zoom_minus_button);
|
||||
zoom_minus_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_zoom_minus));
|
||||
|
||||
|
|
@ -2966,7 +3216,8 @@ GraphEdit::GraphEdit() {
|
|||
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);
|
||||
zoom_reset_button->set_accessibility_name(ETR("Zoom Reset"));
|
||||
zoom_reset_button->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
menu_hbox->add_child(zoom_reset_button);
|
||||
zoom_reset_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_zoom_reset));
|
||||
|
||||
|
|
@ -2974,7 +3225,8 @@ GraphEdit::GraphEdit() {
|
|||
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);
|
||||
zoom_plus_button->set_accessibility_name(ETR("Zoom In"));
|
||||
zoom_plus_button->set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
menu_hbox->add_child(zoom_plus_button);
|
||||
zoom_plus_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_zoom_plus));
|
||||
|
||||
|
|
@ -2986,6 +3238,7 @@ GraphEdit::GraphEdit() {
|
|||
toggle_grid_button->set_toggle_mode(true);
|
||||
toggle_grid_button->set_pressed(true);
|
||||
toggle_grid_button->set_tooltip_text(ETR("Toggle the visual grid."));
|
||||
toggle_grid_button->set_accessibility_name(ETR("Grid"));
|
||||
toggle_grid_button->set_focus_mode(FOCUS_NONE);
|
||||
menu_hbox->add_child(toggle_grid_button);
|
||||
toggle_grid_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::_show_grid_toggled));
|
||||
|
|
@ -2995,6 +3248,7 @@ GraphEdit::GraphEdit() {
|
|||
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."));
|
||||
toggle_snapping_button->set_accessibility_name(ETR("Snap to Grid"));
|
||||
toggle_snapping_button->set_pressed(snapping_enabled);
|
||||
toggle_snapping_button->set_focus_mode(FOCUS_NONE);
|
||||
menu_hbox->add_child(toggle_snapping_button);
|
||||
|
|
@ -3007,6 +3261,7 @@ GraphEdit::GraphEdit() {
|
|||
snapping_distance_spinbox->set_step(1);
|
||||
snapping_distance_spinbox->set_value(snapping_distance);
|
||||
snapping_distance_spinbox->set_tooltip_text(ETR("Change the snapping distance."));
|
||||
snapping_distance_spinbox->set_accessibility_name(ETR("Snapping Distance"));
|
||||
menu_hbox->add_child(snapping_distance_spinbox);
|
||||
snapping_distance_spinbox->connect(SceneStringName(value_changed), callable_mp(this, &GraphEdit::_snapping_distance_changed));
|
||||
|
||||
|
|
@ -3017,6 +3272,7 @@ GraphEdit::GraphEdit() {
|
|||
minimap_button->set_visible(show_minimap_button);
|
||||
minimap_button->set_toggle_mode(true);
|
||||
minimap_button->set_tooltip_text(ETR("Toggle the graph minimap."));
|
||||
minimap_button->set_accessibility_name(ETR("Minimap"));
|
||||
minimap_button->set_pressed(show_grid);
|
||||
minimap_button->set_focus_mode(FOCUS_NONE);
|
||||
menu_hbox->add_child(minimap_button);
|
||||
|
|
@ -3025,6 +3281,7 @@ GraphEdit::GraphEdit() {
|
|||
arrange_button = memnew(Button);
|
||||
arrange_button->set_theme_type_variation(SceneStringName(FlatButton));
|
||||
arrange_button->set_visible(show_arrange_button);
|
||||
arrange_button->set_accessibility_name(ETR("Auto Arrange"));
|
||||
arrange_button->connect(SceneStringName(pressed), callable_mp(this, &GraphEdit::arrange_nodes));
|
||||
arrange_button->set_focus_mode(FOCUS_NONE);
|
||||
menu_hbox->add_child(arrange_button);
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GRAPH_EDIT_H
|
||||
#define GRAPH_EDIT_H
|
||||
#pragma once
|
||||
|
||||
#include "core/variant/typed_dictionary.h"
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/graph_frame.h"
|
||||
#include "scene/gui/graph_node.h"
|
||||
|
|
@ -199,6 +199,7 @@ private:
|
|||
bool show_grid = true;
|
||||
GridPattern grid_pattern = GRID_PATTERN_LINES;
|
||||
|
||||
bool keyboard_connecting = false;
|
||||
bool connecting = false;
|
||||
StringName connecting_from_node;
|
||||
bool connecting_from_output = false;
|
||||
|
|
@ -270,6 +271,7 @@ private:
|
|||
float base_scale = 1.0;
|
||||
|
||||
Ref<StyleBox> panel;
|
||||
Ref<StyleBox> panel_focus;
|
||||
Color grid_major;
|
||||
Color grid_minor;
|
||||
|
||||
|
|
@ -304,6 +306,8 @@ private:
|
|||
HashMap<StringName, HashSet<StringName>> frame_attached_nodes;
|
||||
HashMap<StringName, StringName> linked_parent_map;
|
||||
|
||||
Dictionary type_names;
|
||||
|
||||
void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event);
|
||||
void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event);
|
||||
|
||||
|
|
@ -345,6 +349,7 @@ private:
|
|||
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;
|
||||
TypedArray<Dictionary> _get_connection_list_from_node(const StringName &p_node) const;
|
||||
|
||||
Rect2 _compute_shrinked_frame_rect(const GraphFrame *p_frame);
|
||||
void _set_drag_frame_attached_nodes(GraphFrame *p_frame, bool p_drag);
|
||||
|
|
@ -404,6 +409,9 @@ public:
|
|||
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);
|
||||
GraphNode *get_input_connection_target(const StringName &p_node, int p_port);
|
||||
GraphNode *get_output_connection_target(const StringName &p_node, int p_port);
|
||||
String get_connections_description(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 force_connection_drag_end();
|
||||
|
|
@ -413,6 +421,13 @@ public:
|
|||
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;
|
||||
|
||||
bool is_keyboard_connecting() const { return keyboard_connecting; }
|
||||
void start_keyboard_connecting(GraphNode *p_node, int p_in_port, int p_out_port);
|
||||
void end_keyboard_connecting(GraphNode *p_node, int p_in_port, int p_out_port);
|
||||
|
||||
Dictionary get_type_names() const;
|
||||
void set_type_names(const Dictionary &p_names);
|
||||
|
||||
virtual bool is_node_hover_valid(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
|
||||
|
||||
void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity);
|
||||
|
|
@ -517,5 +532,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(GraphEdit::PanningScheme);
|
||||
VARIANT_ENUM_CAST(GraphEdit::GridPattern);
|
||||
|
||||
#endif // GRAPH_EDIT_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GRAPH_EDIT_ARRANGER_H
|
||||
#define GRAPH_EDIT_ARRANGER_H
|
||||
#pragma once
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
|
|
@ -63,5 +62,3 @@ public:
|
|||
GraphEditArranger(GraphEdit *p_graph_edit) :
|
||||
graph_edit(p_graph_edit) {}
|
||||
};
|
||||
|
||||
#endif // GRAPH_EDIT_ARRANGER_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GRAPH_ELEMENT_H
|
||||
#define GRAPH_ELEMENT_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -93,5 +92,3 @@ public:
|
|||
|
||||
GraphElement() {}
|
||||
};
|
||||
|
||||
#endif // GRAPH_ELEMENT_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GRAPH_FRAME_H
|
||||
#define GRAPH_FRAME_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/graph_element.h"
|
||||
|
||||
|
|
@ -105,5 +104,3 @@ public:
|
|||
|
||||
GraphFrame();
|
||||
};
|
||||
|
||||
#endif // GRAPH_FRAME_H
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "graph_node.h"
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
|
|
@ -41,8 +42,8 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
|
|||
return false;
|
||||
}
|
||||
|
||||
int idx = str.get_slice("/", 1).to_int();
|
||||
String slot_property_name = str.get_slice("/", 2);
|
||||
int idx = str.get_slicec('/', 1).to_int();
|
||||
String slot_property_name = str.get_slicec('/', 2);
|
||||
|
||||
Slot slot;
|
||||
if (slot_table.has(idx)) {
|
||||
|
|
@ -93,8 +94,8 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
int idx = str.get_slice("/", 1).to_int();
|
||||
StringName slot_property_name = str.get_slice("/", 2);
|
||||
int idx = str.get_slicec('/', 1).to_int();
|
||||
StringName slot_property_name = str.get_slicec('/', 2);
|
||||
|
||||
Slot slot;
|
||||
if (slot_table.has(idx)) {
|
||||
|
|
@ -196,6 +197,10 @@ void GraphNode::_resort() {
|
|||
|
||||
children_count++;
|
||||
}
|
||||
slot_count = children_count;
|
||||
if (selected_slot >= slot_count) {
|
||||
selected_slot = -1;
|
||||
}
|
||||
|
||||
if (children_count == 0) {
|
||||
return;
|
||||
|
|
@ -279,12 +284,13 @@ void GraphNode::_resort() {
|
|||
Rect2 rect(margin, from_y_pos, final_width, height);
|
||||
fit_child_in_rect(child, rect);
|
||||
|
||||
slot_y_cache.push_back(from_y_pos - sb_panel->get_margin(SIDE_TOP) + height * 0.5);
|
||||
slot_y_cache.push_back(child->get_rect().position.y + child->get_rect().size.height * 0.5);
|
||||
|
||||
ofs_y = to_y_pos;
|
||||
valid_children_idx++;
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
port_pos_dirty = true;
|
||||
}
|
||||
|
|
@ -306,8 +312,311 @@ void GraphNode::draw_port(int p_slot_index, Point2i p_pos, bool p_left, const Co
|
|||
port_icon->draw(get_canvas_item(), p_pos + icon_offset, p_color);
|
||||
}
|
||||
|
||||
void GraphNode::_accessibility_action_slot(const Variant &p_data) {
|
||||
CustomAccessibilityAction action = (CustomAccessibilityAction)p_data.operator int();
|
||||
switch (action) {
|
||||
case ACTION_CONNECT_INPUT: {
|
||||
if (slot_table.has(selected_slot)) {
|
||||
const Slot &slot = slot_table[selected_slot];
|
||||
if (slot.enable_left) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph) {
|
||||
for (int i = 0; i < left_port_cache.size(); i++) {
|
||||
if (left_port_cache[i].slot_index == selected_slot) {
|
||||
if (graph->is_keyboard_connecting()) {
|
||||
graph->end_keyboard_connecting(this, i, -1);
|
||||
} else {
|
||||
graph->start_keyboard_connecting(this, i, -1);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case ACTION_CONNECT_OUTPUT: {
|
||||
if (slot_table.has(selected_slot)) {
|
||||
const Slot &slot = slot_table[selected_slot];
|
||||
if (slot.enable_right) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph) {
|
||||
for (int i = 0; i < right_port_cache.size(); i++) {
|
||||
if (right_port_cache[i].slot_index == selected_slot) {
|
||||
if (graph->is_keyboard_connecting()) {
|
||||
graph->end_keyboard_connecting(this, -1, i);
|
||||
} else {
|
||||
graph->start_keyboard_connecting(this, -1, i);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case ACTION_FOLLOW_INPUT: {
|
||||
if (slot_table.has(selected_slot)) {
|
||||
const Slot &slot = slot_table[selected_slot];
|
||||
if (slot.enable_left) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph) {
|
||||
for (int i = 0; i < left_port_cache.size(); i++) {
|
||||
if (left_port_cache[i].slot_index == selected_slot) {
|
||||
GraphNode *target = graph->get_input_connection_target(get_name(), i);
|
||||
if (target) {
|
||||
target->grab_focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case ACTION_FOLLOW_OUTPUT: {
|
||||
if (slot_table.has(selected_slot)) {
|
||||
const Slot &slot = slot_table[selected_slot];
|
||||
if (slot.enable_right) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph) {
|
||||
for (int i = 0; i < right_port_cache.size(); i++) {
|
||||
if (right_port_cache[i].slot_index == selected_slot) {
|
||||
GraphNode *target = graph->get_output_connection_target(get_name(), i);
|
||||
if (target) {
|
||||
target->grab_focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void GraphNode::gui_input(const Ref<InputEvent> &p_event) {
|
||||
ERR_FAIL_COND(p_event.is_null());
|
||||
if (port_pos_dirty) {
|
||||
_port_pos_update();
|
||||
}
|
||||
|
||||
if (p_event->is_pressed() && slot_count > 0) {
|
||||
if (p_event->is_action("ui_up", true)) {
|
||||
selected_slot--;
|
||||
if (selected_slot < 0) {
|
||||
selected_slot = -1;
|
||||
} else {
|
||||
accept_event();
|
||||
}
|
||||
} else if (p_event->is_action("ui_down", true)) {
|
||||
selected_slot++;
|
||||
if (selected_slot >= slot_count) {
|
||||
selected_slot = -1;
|
||||
} else {
|
||||
accept_event();
|
||||
}
|
||||
} else if (p_event->is_action("ui_cancel", true)) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph && graph->is_keyboard_connecting()) {
|
||||
graph->force_connection_drag_end();
|
||||
accept_event();
|
||||
}
|
||||
} else if (p_event->is_action("ui_graph_delete", true)) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph && graph->is_keyboard_connecting()) {
|
||||
graph->end_keyboard_connecting(this, -1, -1);
|
||||
accept_event();
|
||||
}
|
||||
} else if (p_event->is_action("ui_graph_follow_left", true)) {
|
||||
if (slot_table.has(selected_slot)) {
|
||||
const Slot &slot = slot_table[selected_slot];
|
||||
if (slot.enable_left) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph) {
|
||||
for (int i = 0; i < left_port_cache.size(); i++) {
|
||||
if (left_port_cache[i].slot_index == selected_slot) {
|
||||
GraphNode *target = graph->get_input_connection_target(get_name(), i);
|
||||
if (target) {
|
||||
target->grab_focus();
|
||||
accept_event();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (p_event->is_action("ui_graph_follow_right", true)) {
|
||||
if (slot_table.has(selected_slot)) {
|
||||
const Slot &slot = slot_table[selected_slot];
|
||||
if (slot.enable_right) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph) {
|
||||
for (int i = 0; i < right_port_cache.size(); i++) {
|
||||
if (right_port_cache[i].slot_index == selected_slot) {
|
||||
GraphNode *target = graph->get_output_connection_target(get_name(), i);
|
||||
if (target) {
|
||||
target->grab_focus();
|
||||
accept_event();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (p_event->is_action("ui_left", true)) {
|
||||
if (slot_table.has(selected_slot)) {
|
||||
const Slot &slot = slot_table[selected_slot];
|
||||
if (slot.enable_left) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph) {
|
||||
for (int i = 0; i < left_port_cache.size(); i++) {
|
||||
if (left_port_cache[i].slot_index == selected_slot) {
|
||||
if (graph->is_keyboard_connecting()) {
|
||||
graph->end_keyboard_connecting(this, i, -1);
|
||||
} else {
|
||||
graph->start_keyboard_connecting(this, i, -1);
|
||||
}
|
||||
accept_event();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (p_event->is_action("ui_right", true)) {
|
||||
if (slot_table.has(selected_slot)) {
|
||||
const Slot &slot = slot_table[selected_slot];
|
||||
if (slot.enable_right) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
if (graph) {
|
||||
for (int i = 0; i < right_port_cache.size(); i++) {
|
||||
if (right_port_cache[i].slot_index == selected_slot) {
|
||||
if (graph->is_keyboard_connecting()) {
|
||||
graph->end_keyboard_connecting(this, -1, i);
|
||||
} else {
|
||||
graph->start_keyboard_connecting(this, -1, i);
|
||||
}
|
||||
accept_event();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (p_event->is_action("ui_accept", true)) {
|
||||
if (slot_table.has(selected_slot)) {
|
||||
int idx = 0;
|
||||
for (int i = 0; i < get_child_count(false); i++) {
|
||||
Control *child = as_sortable_control(get_child(i, false), SortableVisibilityMode::IGNORE);
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
if (idx == selected_slot) {
|
||||
selected_slot = -1;
|
||||
child->grab_focus();
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
accept_event();
|
||||
}
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
GraphElement::gui_input(p_event);
|
||||
}
|
||||
|
||||
void GraphNode::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
String name = get_accessibility_name();
|
||||
if (name.is_empty()) {
|
||||
name = get_name();
|
||||
}
|
||||
name = vformat(ETR("graph node %s (%s)"), name, get_title());
|
||||
|
||||
if (slot_table.has(selected_slot)) {
|
||||
GraphEdit *graph = Object::cast_to<GraphEdit>(get_parent());
|
||||
Dictionary type_info;
|
||||
if (graph) {
|
||||
type_info = graph->get_type_names();
|
||||
}
|
||||
const Slot &slot = slot_table[selected_slot];
|
||||
name += ", " + vformat(ETR("slot %d of %d"), selected_slot + 1, slot_count);
|
||||
if (slot.enable_left) {
|
||||
if (type_info.has(slot.type_left)) {
|
||||
name += "," + vformat(ETR("input port, type: %s"), type_info[slot.type_left]);
|
||||
} else {
|
||||
name += "," + vformat(ETR("input port, type: %d"), slot.type_left);
|
||||
}
|
||||
if (graph) {
|
||||
for (int i = 0; i < left_port_cache.size(); i++) {
|
||||
if (left_port_cache[i].slot_index == selected_slot) {
|
||||
String cd = graph->get_connections_description(get_name(), i);
|
||||
if (cd.is_empty()) {
|
||||
name += " " + ETR("no connections");
|
||||
} else {
|
||||
name += " " + cd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (slot.enable_left) {
|
||||
if (type_info.has(slot.type_right)) {
|
||||
name += "," + vformat(ETR("output port, type: %s"), type_info[slot.type_right]);
|
||||
} else {
|
||||
name += "," + vformat(ETR("output port, type: %d"), slot.type_right);
|
||||
}
|
||||
if (graph) {
|
||||
for (int i = 0; i < right_port_cache.size(); i++) {
|
||||
if (right_port_cache[i].slot_index == selected_slot) {
|
||||
String cd = graph->get_connections_description(get_name(), i);
|
||||
if (cd.is_empty()) {
|
||||
name += " " + ETR("no connections");
|
||||
} else {
|
||||
name += " " + cd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (graph && graph->is_keyboard_connecting()) {
|
||||
name += ", " + ETR("currently selecting target port");
|
||||
}
|
||||
} else {
|
||||
name += ", " + vformat(ETR("has %d slots"), slot_count);
|
||||
}
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_LIST);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(ae, name);
|
||||
DisplayServer::get_singleton()->accessibility_update_add_custom_action(ae, CustomAccessibilityAction::ACTION_CONNECT_INPUT, ETR("Edit Input Port Connection"));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_custom_action(ae, CustomAccessibilityAction::ACTION_CONNECT_OUTPUT, ETR("Edit Output Port Connection"));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_custom_action(ae, CustomAccessibilityAction::ACTION_FOLLOW_INPUT, ETR("Follow Input Port Connection"));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_custom_action(ae, CustomAccessibilityAction::ACTION_FOLLOW_OUTPUT, ETR("Follow Output Port Connection"));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_CUSTOM, callable_mp(this, &GraphNode::_accessibility_action_slot));
|
||||
} break;
|
||||
case NOTIFICATION_FOCUS_EXIT: {
|
||||
selected_slot = -1;
|
||||
queue_redraw();
|
||||
} break;
|
||||
case NOTIFICATION_DRAW: {
|
||||
// Used for layout calculations.
|
||||
Ref<StyleBox> sb_panel = theme_cache.panel;
|
||||
|
|
@ -317,6 +626,7 @@ void GraphNode::_notification(int p_what) {
|
|||
Ref<StyleBox> sb_to_draw_titlebar = selected ? theme_cache.titlebar_selected : theme_cache.titlebar;
|
||||
|
||||
Ref<StyleBox> sb_slot = theme_cache.slot;
|
||||
Ref<StyleBox> sb_slot_selected = theme_cache.slot_selected;
|
||||
|
||||
int port_h_offset = theme_cache.port_h_offset;
|
||||
|
||||
|
|
@ -329,6 +639,10 @@ void GraphNode::_notification(int p_what) {
|
|||
// Draw body (slots area) stylebox.
|
||||
draw_style_box(sb_to_draw_panel, body_rect);
|
||||
|
||||
if (has_focus()) {
|
||||
draw_style_box(theme_cache.panel_focus, body_rect);
|
||||
}
|
||||
|
||||
// Draw title bar stylebox above.
|
||||
draw_style_box(sb_to_draw_titlebar, titlebar_rect);
|
||||
|
||||
|
|
@ -348,12 +662,18 @@ void GraphNode::_notification(int p_what) {
|
|||
|
||||
// Left port.
|
||||
if (slot.enable_left) {
|
||||
draw_port(slot_index, Point2i(port_h_offset, slot_y_cache[E.key] + sb_panel->get_margin(SIDE_TOP)), true, slot.color_left);
|
||||
draw_port(slot_index, Point2i(port_h_offset, slot_y_cache[E.key]), true, slot.color_left);
|
||||
}
|
||||
|
||||
// Right port.
|
||||
if (slot.enable_right) {
|
||||
draw_port(slot_index, Point2i(get_size().x - port_h_offset, slot_y_cache[E.key] + sb_panel->get_margin(SIDE_TOP)), false, slot.color_right);
|
||||
draw_port(slot_index, Point2i(get_size().x - port_h_offset, slot_y_cache[E.key]), false, slot.color_right);
|
||||
}
|
||||
|
||||
if (slot_index == selected_slot) {
|
||||
Size2i port_sz = theme_cache.port->get_size();
|
||||
draw_style_box(sb_slot_selected, Rect2i(port_h_offset - port_sz.x, slot_y_cache[E.key] + sb_panel->get_margin(SIDE_TOP) - port_sz.y, port_sz.x * 2, port_sz.y * 2));
|
||||
draw_style_box(sb_slot_selected, Rect2i(get_size().x - port_h_offset - port_sz.x, slot_y_cache[E.key] + sb_panel->get_margin(SIDE_TOP) - port_sz.y, port_sz.x * 2, port_sz.y * 2));
|
||||
}
|
||||
|
||||
// Draw slot stylebox.
|
||||
|
|
@ -400,6 +720,8 @@ void GraphNode::set_slot(int p_slot_index, bool p_enable_left, int p_type_left,
|
|||
slot.custom_port_icon_right = p_custom_right;
|
||||
slot.draw_stylebox = p_draw_stylebox;
|
||||
slot_table[p_slot_index] = slot;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
port_pos_dirty = true;
|
||||
|
||||
|
|
@ -408,12 +730,16 @@ void GraphNode::set_slot(int p_slot_index, bool p_enable_left, int p_type_left,
|
|||
|
||||
void GraphNode::clear_slot(int p_slot_index) {
|
||||
slot_table.erase(p_slot_index);
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
port_pos_dirty = true;
|
||||
}
|
||||
|
||||
void GraphNode::clear_all_slots() {
|
||||
slot_table.clear();
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
port_pos_dirty = true;
|
||||
}
|
||||
|
|
@ -433,6 +759,8 @@ void GraphNode::set_slot_enabled_left(int p_slot_index, bool p_enable) {
|
|||
}
|
||||
|
||||
slot_table[p_slot_index].enable_left = p_enable;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
port_pos_dirty = true;
|
||||
|
||||
|
|
@ -447,6 +775,8 @@ void GraphNode::set_slot_type_left(int p_slot_index, int p_type) {
|
|||
}
|
||||
|
||||
slot_table[p_slot_index].type_left = p_type;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
port_pos_dirty = true;
|
||||
|
||||
|
|
@ -517,6 +847,8 @@ void GraphNode::set_slot_enabled_right(int p_slot_index, bool p_enable) {
|
|||
}
|
||||
|
||||
slot_table[p_slot_index].enable_right = p_enable;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
port_pos_dirty = true;
|
||||
|
||||
|
|
@ -531,6 +863,8 @@ void GraphNode::set_slot_type_right(int p_slot_index, int p_type) {
|
|||
}
|
||||
|
||||
slot_table[p_slot_index].type_right = p_type;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
port_pos_dirty = true;
|
||||
|
||||
|
|
@ -646,14 +980,9 @@ Size2 GraphNode::get_minimum_size() const {
|
|||
|
||||
void GraphNode::_port_pos_update() {
|
||||
int edgeofs = theme_cache.port_h_offset;
|
||||
int separation = theme_cache.separation;
|
||||
|
||||
Ref<StyleBox> sb_panel = theme_cache.panel;
|
||||
Ref<StyleBox> sb_titlebar = theme_cache.titlebar;
|
||||
|
||||
left_port_cache.clear();
|
||||
right_port_cache.clear();
|
||||
int vertical_ofs = titlebar_hbox->get_size().height + sb_titlebar->get_minimum_size().height + sb_panel->get_margin(SIDE_TOP);
|
||||
int slot_index = 0;
|
||||
|
||||
for (int i = 0; i < get_child_count(false); i++) {
|
||||
|
|
@ -663,11 +992,12 @@ void GraphNode::_port_pos_update() {
|
|||
}
|
||||
|
||||
Size2i size = child->get_rect().size;
|
||||
Point2 pos = child->get_position();
|
||||
|
||||
if (slot_table.has(slot_index)) {
|
||||
if (slot_table[slot_index].enable_left) {
|
||||
PortCache port_cache;
|
||||
port_cache.pos = Point2i(edgeofs, vertical_ofs + size.height / 2);
|
||||
port_cache.pos = Point2i(edgeofs, pos.y + size.height / 2);
|
||||
port_cache.type = slot_table[slot_index].type_left;
|
||||
port_cache.color = slot_table[slot_index].color_left;
|
||||
port_cache.slot_index = slot_index;
|
||||
|
|
@ -675,7 +1005,7 @@ void GraphNode::_port_pos_update() {
|
|||
}
|
||||
if (slot_table[slot_index].enable_right) {
|
||||
PortCache port_cache;
|
||||
port_cache.pos = Point2i(get_size().width - edgeofs, vertical_ofs + size.height / 2);
|
||||
port_cache.pos = Point2i(get_size().width - edgeofs, pos.y + size.height / 2);
|
||||
port_cache.type = slot_table[slot_index].type_right;
|
||||
port_cache.color = slot_table[slot_index].color_right;
|
||||
port_cache.slot_index = slot_index;
|
||||
|
|
@ -683,10 +1013,12 @@ void GraphNode::_port_pos_update() {
|
|||
}
|
||||
}
|
||||
|
||||
vertical_ofs += separation;
|
||||
vertical_ofs += size.height;
|
||||
slot_index++;
|
||||
}
|
||||
slot_count = slot_index;
|
||||
if (selected_slot >= slot_count) {
|
||||
selected_slot = -1;
|
||||
}
|
||||
|
||||
port_pos_dirty = false;
|
||||
}
|
||||
|
|
@ -781,6 +1113,25 @@ int GraphNode::get_output_port_slot(int p_port_idx) {
|
|||
return right_port_cache[p_port_idx].slot_index;
|
||||
}
|
||||
|
||||
String GraphNode::get_accessibility_container_name(const Node *p_node) const {
|
||||
int idx = 0;
|
||||
for (int i = 0; i < get_child_count(false); i++) {
|
||||
Control *child = as_sortable_control(get_child(i, false), SortableVisibilityMode::IGNORE);
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
if (child == p_node) {
|
||||
String name = get_accessibility_name();
|
||||
if (name.is_empty()) {
|
||||
name = get_name();
|
||||
}
|
||||
return vformat(ETR(", in slot %d of graph node %s (%s)"), idx + 1, name, get_title());
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void GraphNode::set_title(const String &p_title) {
|
||||
if (title == p_title) {
|
||||
return;
|
||||
|
|
@ -890,9 +1241,11 @@ void GraphNode::_bind_methods() {
|
|||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, panel);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, panel_selected);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, panel_focus);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, titlebar);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, titlebar_selected);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, slot);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, slot_selected);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphNode, separation);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, GraphNode, port_h_offset);
|
||||
|
|
@ -910,7 +1263,9 @@ GraphNode::GraphNode() {
|
|||
title_label = memnew(Label);
|
||||
title_label->set_theme_type_variation("GraphNodeTitleLabel");
|
||||
title_label->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
title_label->set_focus_mode(Control::FOCUS_NONE);
|
||||
titlebar_hbox->add_child(title_label);
|
||||
|
||||
set_mouse_filter(MOUSE_FILTER_STOP);
|
||||
set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GRAPH_NODE_H
|
||||
#define GRAPH_NODE_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/graph_element.h"
|
||||
|
||||
|
|
@ -67,6 +66,14 @@ class GraphNode : public GraphElement {
|
|||
int final_size = 0;
|
||||
};
|
||||
|
||||
enum CustomAccessibilityAction {
|
||||
ACTION_CONNECT_INPUT,
|
||||
ACTION_CONNECT_OUTPUT,
|
||||
ACTION_FOLLOW_INPUT,
|
||||
ACTION_FOLLOW_OUTPUT,
|
||||
};
|
||||
void _accessibility_action_slot(const Variant &p_data);
|
||||
|
||||
HBoxContainer *titlebar_hbox = nullptr;
|
||||
Label *title_label = nullptr;
|
||||
|
||||
|
|
@ -78,12 +85,17 @@ class GraphNode : public GraphElement {
|
|||
HashMap<int, Slot> slot_table;
|
||||
Vector<int> slot_y_cache;
|
||||
|
||||
int slot_count = 0;
|
||||
int selected_slot = -1;
|
||||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> panel;
|
||||
Ref<StyleBox> panel_selected;
|
||||
Ref<StyleBox> panel_focus;
|
||||
Ref<StyleBox> titlebar;
|
||||
Ref<StyleBox> titlebar_selected;
|
||||
Ref<StyleBox> slot;
|
||||
Ref<StyleBox> slot_selected;
|
||||
|
||||
int separation = 0;
|
||||
int port_h_offset = 0;
|
||||
|
|
@ -113,6 +125,9 @@ protected:
|
|||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
virtual String get_accessibility_container_name(const Node *p_node) const override;
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
void set_title(const String &p_title);
|
||||
String get_title() const;
|
||||
|
||||
|
|
@ -173,5 +188,3 @@ public:
|
|||
|
||||
GraphNode();
|
||||
};
|
||||
|
||||
#endif // GRAPH_NODE_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GRID_CONTAINER_H
|
||||
#define GRID_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -56,5 +55,3 @@ public:
|
|||
|
||||
GridContainer();
|
||||
};
|
||||
|
||||
#endif // GRID_CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ void ItemList::_shape_text(int p_idx) {
|
|||
}
|
||||
item.text_buf->add_string(item.xl_text, theme_cache.font, theme_cache.font_size, item.language);
|
||||
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
|
||||
item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
|
||||
item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES);
|
||||
} else {
|
||||
item.text_buf->set_break_flags(TextServer::BREAK_NONE);
|
||||
}
|
||||
|
|
@ -64,6 +64,7 @@ int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bo
|
|||
items.write[item_id].xl_text = _atr(item_id, p_item);
|
||||
_shape_text(item_id);
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
notify_property_list_changed();
|
||||
|
|
@ -77,6 +78,7 @@ int ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) {
|
|||
items.push_back(item);
|
||||
int item_id = items.size() - 1;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
notify_property_list_changed();
|
||||
|
|
@ -96,6 +98,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_idx, p_text);
|
||||
_shape_text(p_idx);
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
}
|
||||
|
|
@ -114,6 +117,7 @@ void ItemList::set_item_text_direction(int p_idx, Control::TextDirection p_text_
|
|||
if (items[p_idx].text_direction != p_text_direction) {
|
||||
items.write[p_idx].text_direction = p_text_direction;
|
||||
_shape_text(p_idx);
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -131,6 +135,7 @@ void ItemList::set_item_language(int p_idx, const String &p_language) {
|
|||
if (items[p_idx].language != p_language) {
|
||||
items.write[p_idx].language = p_language;
|
||||
_shape_text(p_idx);
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -149,6 +154,7 @@ void ItemList::set_item_auto_translate_mode(int p_idx, AutoTranslateMode 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_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +169,11 @@ void ItemList::set_item_tooltip_enabled(int p_idx, const bool p_enabled) {
|
|||
p_idx += get_item_count();
|
||||
}
|
||||
ERR_FAIL_INDEX(p_idx, items.size());
|
||||
items.write[p_idx].tooltip_enabled = p_enabled;
|
||||
if (items[p_idx].tooltip_enabled != p_enabled) {
|
||||
items.write[p_idx].tooltip_enabled = p_enabled;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
}
|
||||
|
||||
bool ItemList::is_item_tooltip_enabled(int p_idx) const {
|
||||
|
|
@ -182,6 +192,7 @@ void ItemList::set_item_tooltip(int p_idx, const String &p_tooltip) {
|
|||
}
|
||||
|
||||
items.write[p_idx].tooltip = p_tooltip;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
}
|
||||
|
|
@ -348,6 +359,8 @@ void ItemList::set_item_selectable(int p_idx, bool p_selectable) {
|
|||
ERR_FAIL_INDEX(p_idx, items.size());
|
||||
|
||||
items.write[p_idx].selectable = p_selectable;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
bool ItemList::is_item_selectable(int p_idx) const {
|
||||
|
|
@ -366,6 +379,8 @@ void ItemList::set_item_disabled(int p_idx, bool p_disabled) {
|
|||
}
|
||||
|
||||
items.write[p_idx].disabled = p_disabled;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +418,10 @@ void ItemList::select(int p_idx, bool p_single) {
|
|||
}
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
items.write[i].selected = p_idx == i;
|
||||
if (items.write[i].selected != (p_idx == i)) {
|
||||
items.write[i].selected = (p_idx == i);
|
||||
items.write[i].accessibility_item_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
current = p_idx;
|
||||
|
|
@ -411,8 +429,10 @@ void ItemList::select(int p_idx, bool p_single) {
|
|||
} else {
|
||||
if (items[p_idx].selectable && !items[p_idx].disabled) {
|
||||
items.write[p_idx].selected = true;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
}
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -425,18 +445,24 @@ void ItemList::deselect(int p_idx) {
|
|||
} else {
|
||||
items.write[p_idx].selected = false;
|
||||
}
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void ItemList::deselect_all() {
|
||||
if (items.size() < 1) {
|
||||
if (items.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
items.write[i].selected = false;
|
||||
if (items.write[i].selected) {
|
||||
items.write[i].selected = false;
|
||||
items.write[i].accessibility_item_dirty = true;
|
||||
}
|
||||
}
|
||||
current = -1;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -457,6 +483,7 @@ void ItemList::set_current(int p_current) {
|
|||
select(p_current, true);
|
||||
} else {
|
||||
current = p_current;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -477,6 +504,7 @@ void ItemList::move_item(int p_from_idx, int p_to_idx) {
|
|||
items.remove_at(p_from_idx);
|
||||
items.insert(p_to_idx, item);
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
notify_property_list_changed();
|
||||
|
|
@ -489,7 +517,17 @@ void ItemList::set_item_count(int p_count) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (items.size() > p_count) {
|
||||
for (int i = p_count; i < items.size(); i++) {
|
||||
if (items[i].accessibility_item_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(items.write[i].accessibility_item_element);
|
||||
items.write[i].accessibility_item_element = RID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items.resize(p_count);
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
notify_property_list_changed();
|
||||
|
|
@ -502,10 +540,15 @@ int ItemList::get_item_count() const {
|
|||
void ItemList::remove_item(int p_idx) {
|
||||
ERR_FAIL_INDEX(p_idx, items.size());
|
||||
|
||||
if (items[p_idx].accessibility_item_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(items.write[p_idx].accessibility_item_element);
|
||||
items.write[p_idx].accessibility_item_element = RID();
|
||||
}
|
||||
items.remove_at(p_idx);
|
||||
if (current == p_idx) {
|
||||
current = -1;
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
defer_select_single = -1;
|
||||
|
|
@ -513,9 +556,16 @@ void ItemList::remove_item(int p_idx) {
|
|||
}
|
||||
|
||||
void ItemList::clear() {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
if (items[i].accessibility_item_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(items.write[i].accessibility_item_element);
|
||||
items.write[i].accessibility_item_element = RID();
|
||||
}
|
||||
}
|
||||
items.clear();
|
||||
current = -1;
|
||||
ensure_selected_visible = false;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
defer_select_single = -1;
|
||||
|
|
@ -558,13 +608,14 @@ void ItemList::set_max_text_lines(int p_lines) {
|
|||
max_text_lines = p_lines;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
|
||||
items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
|
||||
items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES);
|
||||
items.write[i].text_buf->set_max_lines_visible(p_lines);
|
||||
} else {
|
||||
items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE);
|
||||
}
|
||||
}
|
||||
shape_changed = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -581,6 +632,7 @@ void ItemList::set_max_columns(int p_amount) {
|
|||
}
|
||||
|
||||
max_columns = p_amount;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
}
|
||||
|
|
@ -595,6 +647,7 @@ void ItemList::set_select_mode(SelectMode p_mode) {
|
|||
}
|
||||
|
||||
select_mode = p_mode;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -608,7 +661,7 @@ void ItemList::set_icon_mode(IconMode p_mode) {
|
|||
icon_mode = p_mode;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
|
||||
items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
|
||||
items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES);
|
||||
} else {
|
||||
items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE);
|
||||
}
|
||||
|
|
@ -694,7 +747,9 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (mm.is_valid()) {
|
||||
int closest = get_item_at_position(mm->get_position(), true);
|
||||
if (closest != hovered) {
|
||||
prev_hovered = hovered;
|
||||
hovered = closest;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -834,6 +889,21 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
|
||||
if (p_event->is_pressed() && items.size() > 0) {
|
||||
if (p_event->is_action("ui_menu", true)) {
|
||||
if (current != -1 && allow_rmb_select) {
|
||||
int i = current;
|
||||
|
||||
if (items[i].disabled) {
|
||||
// Don't emit any signal or do any action with clicked item when disabled.
|
||||
return;
|
||||
}
|
||||
|
||||
emit_signal(SNAME("item_clicked"), i, get_item_rect(i).position, MouseButton::RIGHT);
|
||||
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (p_event->is_action("ui_up", true)) {
|
||||
if (!search_string.is_empty()) {
|
||||
uint64_t now = OS::get_singleton()->get_ticks_msec();
|
||||
|
|
@ -1076,8 +1146,131 @@ static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) {
|
|||
return Rect2(ofs_x, ofs_y, tex_width, tex_height);
|
||||
}
|
||||
|
||||
RID ItemList::get_focused_accessibility_element() const {
|
||||
if (current == -1) {
|
||||
return get_accessibility_element();
|
||||
} else {
|
||||
const Item &item = items[current];
|
||||
return item.accessibility_item_element;
|
||||
}
|
||||
}
|
||||
|
||||
void ItemList::_accessibility_action_scroll_set(const Variant &p_data) {
|
||||
const Point2 &pos = p_data;
|
||||
scroll_bar_h->set_value(pos.x);
|
||||
scroll_bar_v->set_value(pos.y);
|
||||
}
|
||||
|
||||
void ItemList::_accessibility_action_scroll_up(const Variant &p_data) {
|
||||
scroll_bar_v->set_value(scroll_bar_v->get_value() - scroll_bar_v->get_page() / 4);
|
||||
}
|
||||
|
||||
void ItemList::_accessibility_action_scroll_down(const Variant &p_data) {
|
||||
scroll_bar_v->set_value(scroll_bar_v->get_value() + scroll_bar_v->get_page() / 4);
|
||||
}
|
||||
|
||||
void ItemList::_accessibility_action_scroll_left(const Variant &p_data) {
|
||||
scroll_bar_h->set_value(scroll_bar_h->get_value() - scroll_bar_h->get_page() / 4);
|
||||
}
|
||||
|
||||
void ItemList::_accessibility_action_scroll_right(const Variant &p_data) {
|
||||
scroll_bar_h->set_value(scroll_bar_h->get_value() + scroll_bar_h->get_page() / 4);
|
||||
}
|
||||
|
||||
void ItemList::_accessibility_action_scroll_into_view(const Variant &p_data, int p_index) {
|
||||
ERR_FAIL_INDEX(p_index, items.size());
|
||||
|
||||
Rect2 r = items[p_index].rect_cache;
|
||||
int from_v = scroll_bar_v->get_value();
|
||||
int to_v = from_v + scroll_bar_v->get_page();
|
||||
int from_h = scroll_bar_h->get_value();
|
||||
int to_h = from_h + scroll_bar_h->get_page();
|
||||
|
||||
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));
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
void ItemList::_accessibility_action_focus(const Variant &p_data, int p_index) {
|
||||
select(p_index);
|
||||
}
|
||||
|
||||
void ItemList::_accessibility_action_blur(const Variant &p_data, int p_index) {
|
||||
deselect(p_index);
|
||||
}
|
||||
|
||||
void ItemList::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
items.write[i].accessibility_item_element = RID();
|
||||
}
|
||||
accessibility_scroll_element = RID();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
force_update_list_size();
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_LIST_BOX);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_count(ae, items.size());
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(ae, DisplayServer::AccessibilityFlags::FLAG_MULTISELECTABLE, select_mode == SELECT_MULTI);
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_DOWN, callable_mp(this, &ItemList::_accessibility_action_scroll_down));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_UP, callable_mp(this, &ItemList::_accessibility_action_scroll_up));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_LEFT, callable_mp(this, &ItemList::_accessibility_action_scroll_left));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_RIGHT, callable_mp(this, &ItemList::_accessibility_action_scroll_right));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SET_SCROLL_OFFSET, callable_mp(this, &ItemList::_accessibility_action_scroll_set));
|
||||
|
||||
if (accessibility_scroll_element.is_null()) {
|
||||
accessibility_scroll_element = DisplayServer::get_singleton()->accessibility_create_sub_element(ae, DisplayServer::AccessibilityRole::ROLE_CONTAINER);
|
||||
}
|
||||
|
||||
Transform2D scroll_xform;
|
||||
scroll_xform.set_origin(Vector2i(-scroll_bar_h->get_value(), -scroll_bar_v->get_value()));
|
||||
DisplayServer::get_singleton()->accessibility_update_set_transform(accessibility_scroll_element, scroll_xform);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_bounds(accessibility_scroll_element, Rect2(0, 0, scroll_bar_h->get_max(), scroll_bar_v->get_max()));
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
const Item &item = items.write[i];
|
||||
|
||||
if (item.accessibility_item_element.is_null()) {
|
||||
item.accessibility_item_element = DisplayServer::get_singleton()->accessibility_create_sub_element(accessibility_scroll_element, DisplayServer::AccessibilityRole::ROLE_LIST_BOX_OPTION);
|
||||
item.accessibility_item_dirty = true;
|
||||
}
|
||||
if (item.accessibility_item_dirty || i == hovered || i == prev_hovered) {
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(item.accessibility_item_element, DisplayServer::AccessibilityAction::ACTION_SCROLL_INTO_VIEW, callable_mp(this, &ItemList::_accessibility_action_scroll_into_view).bind(i));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(item.accessibility_item_element, DisplayServer::AccessibilityAction::ACTION_FOCUS, callable_mp(this, &ItemList::_accessibility_action_focus).bind(i));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(item.accessibility_item_element, DisplayServer::AccessibilityAction::ACTION_BLUR, callable_mp(this, &ItemList::_accessibility_action_blur).bind(i));
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_index(item.accessibility_item_element, i);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_level(item.accessibility_item_element, 0);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_selected(item.accessibility_item_element, item.selected);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(item.accessibility_item_element, item.xl_text);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(item.accessibility_item_element, DisplayServer::AccessibilityFlags::FLAG_DISABLED, item.disabled);
|
||||
if (item.tooltip_enabled) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_tooltip(item.accessibility_item_element, item.tooltip);
|
||||
}
|
||||
|
||||
Rect2 r = get_item_rect(i);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_bounds(item.accessibility_item_element, Rect2(r.position, r.size));
|
||||
|
||||
item.accessibility_item_dirty = false;
|
||||
}
|
||||
}
|
||||
prev_hovered = -1;
|
||||
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_RESIZED: {
|
||||
shape_changed = true;
|
||||
queue_redraw();
|
||||
|
|
@ -1089,6 +1282,7 @@ void ItemList::_notification(int p_what) {
|
|||
_shape_text(i);
|
||||
}
|
||||
shape_changed = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
} break;
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
|
|
@ -1097,6 +1291,7 @@ void ItemList::_notification(int p_what) {
|
|||
_shape_text(i);
|
||||
}
|
||||
shape_changed = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
} break;
|
||||
|
||||
|
|
@ -1537,6 +1732,8 @@ void ItemList::force_update_list_size() {
|
|||
|
||||
items.write[i].rect_cache.size = minsize;
|
||||
items.write[i].min_rect_cache.size = minsize;
|
||||
|
||||
items.write[i].accessibility_item_dirty = true;
|
||||
}
|
||||
|
||||
int fit_size = size.x - theme_cache.panel_style->get_minimum_size().width;
|
||||
|
|
@ -1661,7 +1858,9 @@ void ItemList::_scroll_changed(double) {
|
|||
|
||||
void ItemList::_mouse_exited() {
|
||||
if (hovered > -1) {
|
||||
prev_hovered = hovered;
|
||||
hovered = -1;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -1763,6 +1962,7 @@ String ItemList::get_tooltip(const Point2 &p_pos) const {
|
|||
|
||||
void ItemList::sort_items_by_text() {
|
||||
items.sort();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
shape_changed = true;
|
||||
|
||||
|
|
@ -1872,6 +2072,7 @@ void ItemList::set_auto_width(bool p_enable) {
|
|||
|
||||
auto_width = p_enable;
|
||||
shape_changed = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -1886,6 +2087,7 @@ void ItemList::set_auto_height(bool p_enable) {
|
|||
|
||||
auto_height = p_enable;
|
||||
shape_changed = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -2078,7 +2280,7 @@ void ItemList::_bind_methods() {
|
|||
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::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis (6+ Characters),Word Ellipsis (6+ Characters),Ellipsis (Always),Word Ellipsis (Always)"), "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", "");
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef ITEM_LIST_H
|
||||
#define ITEM_LIST_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/scroll_bar.h"
|
||||
|
|
@ -53,6 +52,9 @@ public:
|
|||
|
||||
private:
|
||||
struct Item {
|
||||
mutable RID accessibility_item_element;
|
||||
mutable bool accessibility_item_dirty = true;
|
||||
|
||||
Ref<Texture2D> icon;
|
||||
bool icon_transposed = false;
|
||||
Rect2i icon_region;
|
||||
|
|
@ -88,12 +90,14 @@ private:
|
|||
|
||||
Item(bool p_dummy) {}
|
||||
};
|
||||
RID accessibility_scroll_element;
|
||||
|
||||
static inline PropertyListHelper base_property_helper;
|
||||
PropertyListHelper property_helper;
|
||||
|
||||
int current = -1;
|
||||
int hovered = -1;
|
||||
int prev_hovered = -1;
|
||||
|
||||
bool shape_changed = true;
|
||||
|
||||
|
|
@ -181,7 +185,18 @@ protected:
|
|||
bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); }
|
||||
static void _bind_methods();
|
||||
|
||||
void _accessibility_action_scroll_set(const Variant &p_data);
|
||||
void _accessibility_action_scroll_up(const Variant &p_data);
|
||||
void _accessibility_action_scroll_down(const Variant &p_data);
|
||||
void _accessibility_action_scroll_left(const Variant &p_data);
|
||||
void _accessibility_action_scroll_right(const Variant &p_data);
|
||||
void _accessibility_action_scroll_into_view(const Variant &p_data, int p_index);
|
||||
void _accessibility_action_focus(const Variant &p_data, int p_index);
|
||||
void _accessibility_action_blur(const Variant &p_data, int p_index);
|
||||
|
||||
public:
|
||||
virtual RID get_focused_accessibility_element() const override;
|
||||
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
int add_item(const String &p_item, const Ref<Texture2D> &p_texture = Ref<Texture2D>(), bool p_selectable = true);
|
||||
|
|
@ -325,5 +340,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(ItemList::SelectMode);
|
||||
VARIANT_ENUM_CAST(ItemList::IconMode);
|
||||
|
||||
#endif // ITEM_LIST_H
|
||||
|
|
|
|||
|
|
@ -55,6 +55,27 @@ TextServer::AutowrapMode Label::get_autowrap_mode() const {
|
|||
return autowrap_mode;
|
||||
}
|
||||
|
||||
void Label::set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags) {
|
||||
if (autowrap_flags_trim == (p_flags & TextServer::BREAK_TRIM_MASK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
autowrap_flags_trim = p_flags & TextServer::BREAK_TRIM_MASK;
|
||||
for (Paragraph ¶ : paragraphs) {
|
||||
para.lines_dirty = true;
|
||||
}
|
||||
queue_redraw();
|
||||
update_configuration_warnings();
|
||||
|
||||
if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
|
||||
update_minimum_size();
|
||||
}
|
||||
}
|
||||
|
||||
BitField<TextServer::LineBreakFlag> Label::get_autowrap_trim_flags() const {
|
||||
return autowrap_flags_trim;
|
||||
}
|
||||
|
||||
void Label::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) {
|
||||
if (jst_flags == p_flags) {
|
||||
return;
|
||||
|
|
@ -79,6 +100,7 @@ void Label::set_uppercase(bool p_uppercase) {
|
|||
uppercase = p_uppercase;
|
||||
text_dirty = true;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +218,7 @@ void Label::_shape() const {
|
|||
case TextServer::AUTOWRAP_OFF:
|
||||
break;
|
||||
}
|
||||
autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
|
||||
autowrap_flags = autowrap_flags | autowrap_flags_trim;
|
||||
|
||||
PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(para.text_rid, width, 0, autowrap_flags);
|
||||
for (int i = 0; i < line_breaks.size(); i = i + 2) {
|
||||
|
|
@ -236,6 +258,17 @@ void Label::_shape() const {
|
|||
if (para.lines_dirty) {
|
||||
BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM;
|
||||
switch (overrun_behavior) {
|
||||
case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS_FORCE: {
|
||||
overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
|
||||
overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY);
|
||||
overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS);
|
||||
overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS);
|
||||
} break;
|
||||
case TextServer::OVERRUN_TRIM_ELLIPSIS_FORCE: {
|
||||
overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
|
||||
overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS);
|
||||
overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS);
|
||||
} break;
|
||||
case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS:
|
||||
overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
|
||||
overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY);
|
||||
|
|
@ -392,14 +425,15 @@ inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_fo
|
|||
}
|
||||
}
|
||||
|
||||
inline void draw_glyph_shadow(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_shadow_color, int p_shadow_outline_size, const Vector2 &p_ofs, const Vector2 &shadow_ofs) {
|
||||
inline void draw_glyph_shadow(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_shadow_color, const Vector2 &p_ofs, const Vector2 &shadow_ofs) {
|
||||
if (p_gl.font_rid != RID()) {
|
||||
if (p_font_shadow_color.a > 0) {
|
||||
TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
|
||||
}
|
||||
if (p_font_shadow_color.a > 0 && p_shadow_outline_size > 0) {
|
||||
TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
|
||||
}
|
||||
TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
|
||||
}
|
||||
}
|
||||
|
||||
inline void draw_glyph_shadow_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_shadow_color, int p_shadow_outline_size, const Vector2 &p_ofs, const Vector2 &shadow_ofs) {
|
||||
if (p_gl.font_rid != RID()) {
|
||||
TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -659,6 +693,15 @@ PackedStringArray Label::get_configuration_warnings() const {
|
|||
|
||||
void Label::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_value(ae, xl_text);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_text_align(ae, horizontal_alignment);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
String new_text = atr(text);
|
||||
if (new_text == xl_text) {
|
||||
|
|
@ -670,6 +713,7 @@ void Label::_notification(int p_what) {
|
|||
}
|
||||
text_dirty = true;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_configuration_warnings();
|
||||
} break;
|
||||
|
|
@ -703,7 +747,6 @@ void Label::_notification(int p_what) {
|
|||
|
||||
bool has_settings = settings.is_valid();
|
||||
|
||||
Size2 string_size;
|
||||
Ref<StyleBox> style = theme_cache.normal_style;
|
||||
Ref<Font> font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font;
|
||||
int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size;
|
||||
|
|
@ -717,7 +760,11 @@ void Label::_notification(int p_what) {
|
|||
int shadow_outline_size = has_settings ? settings->get_shadow_size() : theme_cache.font_shadow_outline_size;
|
||||
bool rtl_layout = is_layout_rtl();
|
||||
|
||||
style->draw(ci, Rect2(Point2(0, 0), get_size()));
|
||||
if (has_focus()) {
|
||||
theme_cache.focus_style->draw(ci, Rect2(Point2(0, 0), get_size()));
|
||||
} else {
|
||||
theme_cache.normal_style->draw(ci, Rect2(Point2(0, 0), get_size()));
|
||||
}
|
||||
|
||||
bool trim_chars = (visible_chars >= 0) && (visible_chars_behavior == TextServer::VC_CHARS_AFTER_SHAPING);
|
||||
bool trim_glyphs_ltr = (visible_chars >= 0) && ((visible_chars_behavior == TextServer::VC_GLYPHS_LTR) || ((visible_chars_behavior == TextServer::VC_GLYPHS_AUTO) && !rtl_layout));
|
||||
|
|
@ -768,7 +815,10 @@ void Label::_notification(int p_what) {
|
|||
|
||||
// Draw shadow, outline and text. Note: Do not merge this into the single loop iteration, to prevent overlaps.
|
||||
int processed_glyphs_step = 0;
|
||||
for (int step = DRAW_STEP_SHADOW; step < DRAW_STEP_MAX; step++) {
|
||||
for (int step = DRAW_STEP_SHADOW_OUTLINE; step < DRAW_STEP_MAX; step++) {
|
||||
if (step == DRAW_STEP_SHADOW_OUTLINE && (font_shadow_color.a == 0 || shadow_outline_size <= 0)) {
|
||||
continue;
|
||||
}
|
||||
if (step == DRAW_STEP_SHADOW && (font_shadow_color.a == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -784,8 +834,10 @@ void Label::_notification(int p_what) {
|
|||
for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
|
||||
bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end + para.start > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs));
|
||||
if (!skip) {
|
||||
if (step == DRAW_STEP_SHADOW) {
|
||||
draw_glyph_shadow(ellipsis_glyphs[gl_idx], ci, font_shadow_color, shadow_outline_size, offset_step, shadow_ofs);
|
||||
if (step == DRAW_STEP_SHADOW_OUTLINE) {
|
||||
draw_glyph_shadow_outline(ellipsis_glyphs[gl_idx], ci, font_shadow_color, shadow_outline_size, offset_step, shadow_ofs);
|
||||
} else if (step == DRAW_STEP_SHADOW) {
|
||||
draw_glyph_shadow(ellipsis_glyphs[gl_idx], ci, font_shadow_color, offset_step, shadow_ofs);
|
||||
} else if (step == DRAW_STEP_OUTLINE) {
|
||||
draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_outline_color, outline_size, offset_step);
|
||||
} else if (step == DRAW_STEP_TEXT) {
|
||||
|
|
@ -814,8 +866,10 @@ void Label::_notification(int p_what) {
|
|||
for (int k = 0; k < glyphs[j].repeat; k++) {
|
||||
bool skip = (trim_chars && glyphs[j].end + para.start > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs));
|
||||
if (!skip) {
|
||||
if (step == DRAW_STEP_SHADOW) {
|
||||
draw_glyph_shadow(glyphs[j], ci, font_shadow_color, shadow_outline_size, offset_step, shadow_ofs);
|
||||
if (step == DRAW_STEP_SHADOW_OUTLINE) {
|
||||
draw_glyph_shadow_outline(glyphs[j], ci, font_shadow_color, shadow_outline_size, offset_step, shadow_ofs);
|
||||
} else if (step == DRAW_STEP_SHADOW) {
|
||||
draw_glyph_shadow(glyphs[j], ci, font_shadow_color, offset_step, shadow_ofs);
|
||||
} else if (step == DRAW_STEP_OUTLINE) {
|
||||
draw_glyph_outline(glyphs[j], ci, font_outline_color, outline_size, offset_step);
|
||||
} else if (step == DRAW_STEP_TEXT) {
|
||||
|
|
@ -832,8 +886,10 @@ void Label::_notification(int p_what) {
|
|||
for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
|
||||
bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end + para.start > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs));
|
||||
if (!skip) {
|
||||
if (step == DRAW_STEP_SHADOW) {
|
||||
draw_glyph_shadow(ellipsis_glyphs[gl_idx], ci, font_shadow_color, shadow_outline_size, offset_step, shadow_ofs);
|
||||
if (step == DRAW_STEP_SHADOW_OUTLINE) {
|
||||
draw_glyph_shadow_outline(ellipsis_glyphs[gl_idx], ci, font_shadow_color, shadow_outline_size, offset_step, shadow_ofs);
|
||||
} else if (step == DRAW_STEP_SHADOW) {
|
||||
draw_glyph_shadow(ellipsis_glyphs[gl_idx], ci, font_shadow_color, offset_step, shadow_ofs);
|
||||
} else if (step == DRAW_STEP_OUTLINE) {
|
||||
draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_outline_color, outline_size, offset_step);
|
||||
} else if (step == DRAW_STEP_TEXT) {
|
||||
|
|
@ -940,7 +996,7 @@ Size2 Label::get_minimum_size() const {
|
|||
const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font;
|
||||
int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size;
|
||||
|
||||
min_size.height = MAX(min_size.height, font->get_height(font_size) + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM));
|
||||
min_size.height = MAX(min_size.height, font->get_height(font_size));
|
||||
|
||||
Size2 min_style = theme_cache.normal_style->get_minimum_size();
|
||||
if (autowrap_mode != TextServer::AUTOWRAP_OFF) {
|
||||
|
|
@ -1029,7 +1085,7 @@ void Label::set_horizontal_alignment(HorizontalAlignment p_alignment) {
|
|||
}
|
||||
}
|
||||
horizontal_alignment = p_alignment;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -1062,6 +1118,7 @@ void Label::set_text(const String &p_string) {
|
|||
if (visible_ratio < 1) {
|
||||
visible_chars = get_total_character_count() * visible_ratio;
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
update_configuration_warnings();
|
||||
|
|
@ -1152,6 +1209,7 @@ void Label::set_paragraph_separator(const String &p_paragraph_separator) {
|
|||
if (paragraph_separator != p_paragraph_separator) {
|
||||
paragraph_separator = p_paragraph_separator;
|
||||
text_dirty = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -1244,6 +1302,7 @@ void Label::set_visible_characters(int p_amount) {
|
|||
}
|
||||
if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
|
||||
text_dirty = true;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
queue_redraw();
|
||||
}
|
||||
|
|
@ -1268,6 +1327,7 @@ void Label::set_visible_ratio(float p_ratio) {
|
|||
|
||||
if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
|
||||
text_dirty = true;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
queue_redraw();
|
||||
}
|
||||
|
|
@ -1285,6 +1345,7 @@ void Label::set_visible_characters_behavior(TextServer::VisibleCharactersBehavio
|
|||
if (visible_chars_behavior != p_behavior) {
|
||||
if (visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING || p_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
|
||||
text_dirty = true;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
visible_chars_behavior = p_behavior;
|
||||
queue_redraw();
|
||||
|
|
@ -1342,6 +1403,8 @@ void Label::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_paragraph_separator"), &Label::get_paragraph_separator);
|
||||
ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &Label::set_autowrap_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &Label::get_autowrap_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_autowrap_trim_flags", "autowrap_trim_flags"), &Label::set_autowrap_trim_flags);
|
||||
ClassDB::bind_method(D_METHOD("get_autowrap_trim_flags"), &Label::get_autowrap_trim_flags);
|
||||
ClassDB::bind_method(D_METHOD("set_justification_flags", "justification_flags"), &Label::set_justification_flags);
|
||||
ClassDB::bind_method(D_METHOD("get_justification_flags"), &Label::get_justification_flags);
|
||||
ClassDB::bind_method(D_METHOD("set_clip_text", "enable"), &Label::set_clip_text);
|
||||
|
|
@ -1380,11 +1443,12 @@ void Label::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_trim_flags", PROPERTY_HINT_FLAGS, vformat("Trim Spaces After Break:%d,Trim Spaces Before Break:%d", TextServer::BREAK_TRIM_START_EDGE_SPACES, TextServer::BREAK_TRIM_END_EDGE_SPACES)), "set_autowrap_trim_flags", "get_autowrap_trim_flags");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification:1,Word Justification:2,Justify Only After Last Tab:8,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128"), "set_justification_flags", "get_justification_flags");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "paragraph_separator"), "set_paragraph_separator", "get_paragraph_separator");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis (6+ Characters),Word Ellipsis (6+ Characters),Ellipsis (Always),Word Ellipsis (Always)"), "set_text_overrun_behavior", "get_text_overrun_behavior");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "ellipsis_char"), "set_ellipsis_char", "get_ellipsis_char");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "tab_stops"), "set_tab_stops", "get_tab_stops");
|
||||
|
|
@ -1404,6 +1468,7 @@ void Label::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Label, normal_style, "normal");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, Label, focus_style, "focus");
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Label, line_spacing);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Label, paragraph_spacing);
|
||||
|
||||
|
|
@ -1419,6 +1484,7 @@ void Label::_bind_methods() {
|
|||
}
|
||||
|
||||
Label::Label(const String &p_text) {
|
||||
set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
set_mouse_filter(MOUSE_FILTER_IGNORE);
|
||||
set_text(p_text);
|
||||
set_v_size_flags(SIZE_SHRINK_CENTER);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LABEL_H
|
||||
#define LABEL_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/resources/label_settings.h"
|
||||
|
|
@ -39,6 +38,7 @@ class Label : public Control {
|
|||
|
||||
private:
|
||||
enum LabelDrawStep {
|
||||
DRAW_STEP_SHADOW_OUTLINE,
|
||||
DRAW_STEP_SHADOW,
|
||||
DRAW_STEP_OUTLINE,
|
||||
DRAW_STEP_TEXT,
|
||||
|
|
@ -50,6 +50,7 @@ private:
|
|||
String text;
|
||||
String xl_text;
|
||||
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF;
|
||||
BitField<TextServer::LineBreakFlag> autowrap_flags_trim = TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES;
|
||||
BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_SKIP_LAST_LINE | TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE;
|
||||
bool clip = false;
|
||||
String el_char = U"…";
|
||||
|
|
@ -89,6 +90,7 @@ private:
|
|||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> normal_style;
|
||||
Ref<StyleBox> focus_style;
|
||||
Ref<Font> font;
|
||||
|
||||
int font_size = 0;
|
||||
|
|
@ -153,6 +155,9 @@ public:
|
|||
void set_autowrap_mode(TextServer::AutowrapMode p_mode);
|
||||
TextServer::AutowrapMode get_autowrap_mode() const;
|
||||
|
||||
void set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags);
|
||||
BitField<TextServer::LineBreakFlag> get_autowrap_trim_flags() const;
|
||||
|
||||
void set_justification_flags(BitField<TextServer::JustificationFlag> p_flags);
|
||||
BitField<TextServer::JustificationFlag> get_justification_flags() const;
|
||||
|
||||
|
|
@ -196,5 +201,3 @@ public:
|
|||
Label(const String &p_text = String());
|
||||
~Label();
|
||||
};
|
||||
|
||||
#endif // LABEL_H
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "core/input/input_map.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/translation_server.h"
|
||||
#include "scene/gui/label.h"
|
||||
#include "scene/main/window.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
|
@ -399,7 +400,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
if (context_menu_enabled) {
|
||||
_update_context_menu();
|
||||
menu->set_position(get_screen_position() + get_local_mouse_position());
|
||||
menu->set_position(get_screen_transform().xform(get_local_mouse_position()));
|
||||
menu->reset_size();
|
||||
menu->popup();
|
||||
}
|
||||
|
|
@ -486,6 +487,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (!pass && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
|
||||
DisplayServer::get_singleton()->clipboard_set_primary(text);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
} else if (b->is_double_click()) {
|
||||
// Double-click select word.
|
||||
last_dblclk = OS::get_singleton()->get_ticks_msec();
|
||||
|
|
@ -500,6 +502,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
selection.creating = true;
|
||||
selection.start_column = caret_column;
|
||||
set_caret_column(selection.end);
|
||||
queue_accessibility_update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -630,7 +633,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (k->is_action("ui_menu", true)) {
|
||||
_update_context_menu();
|
||||
Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2);
|
||||
menu->set_position(get_screen_position() + pos);
|
||||
menu->set_position(get_screen_transform().xform(pos));
|
||||
menu->reset_size();
|
||||
menu->popup();
|
||||
menu->grab_focus();
|
||||
|
|
@ -668,13 +671,14 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Start Unicode input (hold).
|
||||
// Start Unicode Alt input (hold).
|
||||
if (k->is_alt_pressed() && k->get_keycode() == Key::KP_ADD && !alt_start && !alt_start_no_hold) {
|
||||
if (selection.enabled) {
|
||||
selection_delete();
|
||||
}
|
||||
alt_start = true;
|
||||
alt_code = 0;
|
||||
alt_mode = ALT_INPUT_UNICODE;
|
||||
ime_text = "u";
|
||||
ime_selection = Vector2i(0, -1);
|
||||
_shape();
|
||||
|
|
@ -690,6 +694,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
alt_start_no_hold = true;
|
||||
alt_code = 0;
|
||||
alt_mode = ALT_INPUT_UNICODE;
|
||||
ime_text = "u";
|
||||
ime_selection = Vector2i(0, -1);
|
||||
_shape();
|
||||
|
|
@ -698,37 +703,101 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Start OEM Alt input (hold).
|
||||
if (k->is_alt_pressed() && k->get_keycode() >= Key::KP_1 && k->get_keycode() <= Key::KP_9 && !alt_start && !alt_start_no_hold) {
|
||||
if (selection.enabled) {
|
||||
selection_delete();
|
||||
}
|
||||
alt_start = true;
|
||||
alt_code = (uint32_t)(k->get_keycode() - Key::KP_0);
|
||||
alt_mode = ALT_INPUT_OEM;
|
||||
ime_text = vformat("o%s", String::num_int64(alt_code, 10));
|
||||
ime_selection = Vector2i(0, -1);
|
||||
_shape();
|
||||
queue_redraw();
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
|
||||
// Start Windows Alt input (hold).
|
||||
if (k->is_alt_pressed() && k->get_keycode() == Key::KP_0 && !alt_start && !alt_start_no_hold) {
|
||||
if (selection.enabled) {
|
||||
selection_delete();
|
||||
}
|
||||
alt_start = true;
|
||||
alt_mode = ALT_INPUT_WIN;
|
||||
alt_code = 0;
|
||||
ime_text = "w";
|
||||
ime_selection = Vector2i(0, -1);
|
||||
_shape();
|
||||
queue_redraw();
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update Unicode input.
|
||||
if (k->is_pressed() && ((k->is_alt_pressed() && alt_start) || alt_start_no_hold)) {
|
||||
if (k->get_keycode() >= Key::KEY_0 && k->get_keycode() <= Key::KEY_9) {
|
||||
alt_code = alt_code << 4;
|
||||
if (alt_mode == ALT_INPUT_UNICODE) {
|
||||
alt_code = alt_code << 4;
|
||||
} else {
|
||||
alt_code = alt_code * 10;
|
||||
}
|
||||
alt_code += (uint32_t)(k->get_keycode() - Key::KEY_0);
|
||||
} else if (k->get_keycode() >= Key::KP_0 && k->get_keycode() <= Key::KP_9) {
|
||||
alt_code = alt_code << 4;
|
||||
if (alt_mode == ALT_INPUT_UNICODE) {
|
||||
alt_code = alt_code << 4;
|
||||
} else {
|
||||
alt_code = alt_code * 10;
|
||||
}
|
||||
alt_code += (uint32_t)(k->get_keycode() - Key::KP_0);
|
||||
} else if (k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) {
|
||||
} else if (alt_mode == ALT_INPUT_UNICODE && k->get_keycode() >= Key::A && k->get_keycode() <= Key::F) {
|
||||
alt_code = alt_code << 4;
|
||||
alt_code += (uint32_t)(k->get_keycode() - Key::A) + 10;
|
||||
} else if ((Key)k->get_unicode() >= Key::KEY_0 && (Key)k->get_unicode() <= Key::KEY_9) {
|
||||
alt_code = alt_code << 4;
|
||||
if (alt_mode == ALT_INPUT_UNICODE) {
|
||||
alt_code = alt_code << 4;
|
||||
} else {
|
||||
alt_code = alt_code * 10;
|
||||
}
|
||||
alt_code += (uint32_t)((Key)k->get_unicode() - Key::KEY_0);
|
||||
} else if ((Key)k->get_unicode() >= Key::A && (Key)k->get_unicode() <= Key::F) {
|
||||
} else if (alt_mode == ALT_INPUT_UNICODE && (Key)k->get_unicode() >= Key::A && (Key)k->get_unicode() <= Key::F) {
|
||||
alt_code = alt_code << 4;
|
||||
alt_code += (uint32_t)((Key)k->get_unicode() - Key::A) + 10;
|
||||
} else if (k->get_physical_keycode() >= Key::KEY_0 && k->get_physical_keycode() <= Key::KEY_9) {
|
||||
alt_code = alt_code << 4;
|
||||
if (alt_mode == ALT_INPUT_UNICODE) {
|
||||
alt_code = alt_code << 4;
|
||||
} else {
|
||||
alt_code = alt_code * 10;
|
||||
}
|
||||
alt_code += (uint32_t)(k->get_physical_keycode() - Key::KEY_0);
|
||||
}
|
||||
if (k->get_keycode() == Key::BACKSPACE) {
|
||||
alt_code = alt_code >> 4;
|
||||
if (alt_mode == ALT_INPUT_UNICODE) {
|
||||
alt_code = alt_code >> 4;
|
||||
} else {
|
||||
alt_code = alt_code / 10;
|
||||
}
|
||||
}
|
||||
if (alt_code > 0x10ffff) {
|
||||
alt_code = 0x10ffff;
|
||||
}
|
||||
if (alt_code > 0) {
|
||||
ime_text = vformat("u%s", String::num_int64(alt_code, 16, true));
|
||||
if (alt_mode == ALT_INPUT_UNICODE) {
|
||||
ime_text = vformat("u%s", String::num_int64(alt_code, 16, true));
|
||||
} else if (alt_mode == ALT_INPUT_OEM) {
|
||||
ime_text = vformat("o%s", String::num_int64(alt_code, 10));
|
||||
} else if (alt_mode == ALT_INPUT_WIN) {
|
||||
ime_text = vformat("w%s", String::num_int64(alt_code, 10));
|
||||
}
|
||||
} else {
|
||||
ime_text = "u";
|
||||
if (alt_mode == ALT_INPUT_UNICODE) {
|
||||
ime_text = "u";
|
||||
} else if (alt_mode == ALT_INPUT_OEM) {
|
||||
ime_text = "o";
|
||||
} else if (alt_mode == ALT_INPUT_WIN) {
|
||||
ime_text = "w";
|
||||
}
|
||||
}
|
||||
ime_selection = Vector2i(0, -1);
|
||||
_shape();
|
||||
|
|
@ -741,11 +810,32 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if ((!k->is_pressed() && alt_start && k->get_keycode() == Key::ALT) || (alt_start_no_hold && (k->is_action("ui_text_submit", true) || k->is_action("ui_accept", true)))) {
|
||||
alt_start = false;
|
||||
alt_start_no_hold = false;
|
||||
if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff && alt_code <= 0x10ffff)) {
|
||||
if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff)) {
|
||||
ime_text = String();
|
||||
ime_selection = Vector2i();
|
||||
char32_t ucodestr[2] = { (char32_t)alt_code, 0 };
|
||||
insert_text_at_caret(ucodestr);
|
||||
if (alt_mode == ALT_INPUT_UNICODE) {
|
||||
if ((alt_code > 0x31 && alt_code < 0xd800) || (alt_code > 0xdfff)) {
|
||||
char32_t ucodestr[2] = { (char32_t)alt_code, 0 };
|
||||
insert_text_at_caret(ucodestr);
|
||||
}
|
||||
} else if (alt_mode == ALT_INPUT_OEM) {
|
||||
if (alt_code > 0x00 && alt_code <= 0xff) {
|
||||
char32_t ucodestr[2] = { alt_code_oem437[alt_code], 0 };
|
||||
insert_text_at_caret(ucodestr);
|
||||
} else if ((alt_code > 0xff && alt_code < 0xd800) || (alt_code > 0xdfff)) {
|
||||
char32_t ucodestr[2] = { (char32_t)alt_code, 0 };
|
||||
insert_text_at_caret(ucodestr);
|
||||
}
|
||||
} else if (alt_mode == ALT_INPUT_WIN) {
|
||||
if (alt_code > 0x00 && alt_code <= 0xff) {
|
||||
char32_t ucodestr[2] = { alt_code_cp1252[alt_code], 0 };
|
||||
insert_text_at_caret(ucodestr);
|
||||
} else if ((alt_code > 0xff && alt_code < 0xd800) || (alt_code > 0xdfff)) {
|
||||
char32_t ucodestr[2] = { (char32_t)alt_code, 0 };
|
||||
insert_text_at_caret(ucodestr);
|
||||
}
|
||||
}
|
||||
alt_mode = ALT_INPUT_NONE;
|
||||
} else {
|
||||
ime_text = String();
|
||||
ime_selection = Vector2i();
|
||||
|
|
@ -760,6 +850,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (alt_start_no_hold && k->is_action("ui_cancel", true)) {
|
||||
alt_start = false;
|
||||
alt_start_no_hold = false;
|
||||
alt_mode = ALT_INPUT_NONE;
|
||||
ime_text = String();
|
||||
ime_selection = Vector2i();
|
||||
_shape();
|
||||
|
|
@ -973,7 +1064,9 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
|
|||
if (p_data.is_string() && is_editable()) {
|
||||
apply_ime();
|
||||
|
||||
set_caret_at_pixel_pos(p_point.x);
|
||||
if (p_point != Vector2(Math::INF, Math::INF)) {
|
||||
set_caret_at_pixel_pos(p_point.x);
|
||||
}
|
||||
int caret_column_tmp = caret_column;
|
||||
bool is_inside_sel = selection.enabled && caret_column >= selection.begin && caret_column <= selection.end;
|
||||
if (Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) {
|
||||
|
|
@ -1009,6 +1102,7 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
|
|||
}
|
||||
text_changed_dirty = true;
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -1035,6 +1129,33 @@ void LineEdit::_update_theme_item_cache() {
|
|||
theme_cache.base_scale = get_theme_default_base_scale();
|
||||
}
|
||||
|
||||
void LineEdit::_accessibility_action_set_selection(const Variant &p_data) {
|
||||
Dictionary new_selection = p_data;
|
||||
int sel_start_pos = new_selection["start_char"];
|
||||
int sel_end_pos = new_selection["end_char"];
|
||||
select(sel_start_pos, sel_end_pos);
|
||||
}
|
||||
|
||||
void LineEdit::_accessibility_action_replace_selected(const Variant &p_data) {
|
||||
String new_text = p_data;
|
||||
insert_text_at_caret(new_text);
|
||||
}
|
||||
|
||||
void LineEdit::_accessibility_action_set_value(const Variant &p_data) {
|
||||
String new_text = p_data;
|
||||
set_text(new_text);
|
||||
}
|
||||
|
||||
void LineEdit::_accessibility_action_menu(const Variant &p_data) {
|
||||
_update_context_menu();
|
||||
|
||||
Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2);
|
||||
menu->set_position(get_screen_position() + pos);
|
||||
menu->reset_size();
|
||||
menu->popup();
|
||||
menu->grab_focus();
|
||||
}
|
||||
|
||||
void LineEdit::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
@ -1049,6 +1170,98 @@ void LineEdit::_notification(int p_what) {
|
|||
}
|
||||
} break;
|
||||
#endif
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
|
||||
accessibility_text_root_element = RID();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_TEXT_FIELD);
|
||||
bool using_placeholder = text.is_empty() && ime_text.is_empty();
|
||||
if (using_placeholder && !placeholder.is_empty()) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_placeholder(ae, atr(placeholder));
|
||||
}
|
||||
if (!placeholder.is_empty() && get_accessibility_name().is_empty()) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(ae, atr(placeholder));
|
||||
}
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(ae, DisplayServer::AccessibilityFlags::FLAG_READONLY, !editable);
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SET_TEXT_SELECTION, callable_mp(this, &LineEdit::_accessibility_action_set_selection));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_REPLACE_SELECTED_TEXT, callable_mp(this, &LineEdit::_accessibility_action_replace_selected));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SET_VALUE, callable_mp(this, &LineEdit::_accessibility_action_set_value));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SHOW_CONTEXT_MENU, callable_mp(this, &LineEdit::_accessibility_action_menu));
|
||||
if (!language.is_empty()) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_language(ae, language);
|
||||
} else {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_language(ae, TranslationServer::get_singleton()->get_tool_locale());
|
||||
}
|
||||
|
||||
bool rtl = is_layout_rtl();
|
||||
Ref<StyleBox> style = theme_cache.normal;
|
||||
Ref<Font> font = theme_cache.font;
|
||||
|
||||
Size2 size = get_size();
|
||||
|
||||
int x_ofs = 0;
|
||||
float text_width = TS->shaped_text_get_size(text_rid).x;
|
||||
float text_height = TS->shaped_text_get_size(text_rid).y;
|
||||
int y_area = size.height - style->get_minimum_size().height;
|
||||
int y_ofs = style->get_offset().y + (y_area - text_height) / 2;
|
||||
|
||||
switch (alignment) {
|
||||
case HORIZONTAL_ALIGNMENT_FILL:
|
||||
case HORIZONTAL_ALIGNMENT_LEFT: {
|
||||
if (rtl) {
|
||||
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - Math::ceil(style->get_margin(SIDE_RIGHT) + (text_width))));
|
||||
} else {
|
||||
x_ofs = style->get_offset().x;
|
||||
}
|
||||
} break;
|
||||
case HORIZONTAL_ALIGNMENT_CENTER: {
|
||||
if (!Math::is_zero_approx(scroll_offset)) {
|
||||
x_ofs = style->get_offset().x;
|
||||
} else {
|
||||
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - (text_width)) / 2);
|
||||
}
|
||||
} break;
|
||||
case HORIZONTAL_ALIGNMENT_RIGHT: {
|
||||
if (rtl) {
|
||||
x_ofs = style->get_offset().x;
|
||||
} else {
|
||||
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - Math::ceil(style->get_margin(SIDE_RIGHT) + (text_width))));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
|
||||
if (right_icon.is_valid() || display_clear_icon) {
|
||||
Ref<Texture2D> r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon;
|
||||
if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
|
||||
if (Math::is_zero_approx(scroll_offset)) {
|
||||
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
|
||||
}
|
||||
} else {
|
||||
x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT));
|
||||
}
|
||||
}
|
||||
|
||||
float text_off_x = x_ofs + scroll_offset;
|
||||
|
||||
if (accessibility_text_root_element.is_null()) {
|
||||
accessibility_text_root_element = DisplayServer::get_singleton()->accessibility_create_sub_text_edit_elements(ae, using_placeholder ? RID() : text_rid, text_height);
|
||||
}
|
||||
|
||||
Transform2D text_xform;
|
||||
text_xform.set_origin(Vector2i(text_off_x, y_ofs));
|
||||
DisplayServer::get_singleton()->accessibility_update_set_transform(accessibility_text_root_element, text_xform);
|
||||
if (selection.enabled) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_text_selection(ae, accessibility_text_root_element, selection.begin, accessibility_text_root_element, selection.end);
|
||||
} else {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_text_selection(ae, accessibility_text_root_element, caret_column, accessibility_text_root_element, caret_column);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_RESIZED: {
|
||||
_fit_to_width();
|
||||
|
|
@ -1202,12 +1415,14 @@ void LineEdit::_notification(int p_what) {
|
|||
if (rect.position.x < x_ofs) {
|
||||
rect.size.x -= (x_ofs - rect.position.x);
|
||||
rect.position.x = x_ofs;
|
||||
} else if (rect.position.x + rect.size.x > ofs_max) {
|
||||
}
|
||||
if (rect.position.x + rect.size.x > ofs_max) {
|
||||
rect.size.x = ofs_max - rect.position.x;
|
||||
}
|
||||
RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_color);
|
||||
}
|
||||
}
|
||||
|
||||
const Glyph *glyphs = TS->shaped_text_get_glyphs(text_rid);
|
||||
int gl_size = TS->shaped_text_get_glyph_count(text_rid);
|
||||
|
||||
|
|
@ -1733,7 +1948,7 @@ void LineEdit::_validate_caret_can_draw() {
|
|||
draw_caret = true;
|
||||
caret_blink_timer = 0.0;
|
||||
}
|
||||
caret_can_draw = editing && (window_has_focus || (menu && menu->has_focus())) && (has_focus() || caret_force_displayed);
|
||||
caret_can_draw = (caret_force_displayed && !is_part_of_edited_scene()) || (editing && (window_has_focus || (menu && menu->has_focus())) && has_focus());
|
||||
}
|
||||
|
||||
void LineEdit::delete_char() {
|
||||
|
|
@ -1920,6 +2135,8 @@ void LineEdit::set_caret_column(int p_column) {
|
|||
|
||||
caret_column = p_column;
|
||||
|
||||
queue_accessibility_update();
|
||||
|
||||
// Fit to window.
|
||||
|
||||
if (!is_inside_tree()) {
|
||||
|
|
@ -1988,6 +2205,7 @@ void LineEdit::set_caret_column(int p_column) {
|
|||
|
||||
scroll_offset = MIN(0, scroll_offset);
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -2016,7 +2234,7 @@ void LineEdit::insert_text_at_caret(String p_text) {
|
|||
}
|
||||
}
|
||||
String pre = text.substr(0, caret_column);
|
||||
String post = text.substr(caret_column, text.length() - caret_column);
|
||||
String post = text.substr(caret_column);
|
||||
text = pre + p_text + post;
|
||||
_shape();
|
||||
TextServer::Direction dir = TS->shaped_text_get_dominant_direction_in_range(text_rid, caret_column, caret_column + p_text.length());
|
||||
|
|
@ -2082,6 +2300,7 @@ void LineEdit::deselect() {
|
|||
selection.enabled = false;
|
||||
selection.creating = false;
|
||||
selection.double_click = false;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -2140,6 +2359,7 @@ void LineEdit::selection_fill_at_caret() {
|
|||
}
|
||||
|
||||
selection.enabled = (selection.begin != selection.end);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void LineEdit::select_all() {
|
||||
|
|
@ -2155,6 +2375,7 @@ void LineEdit::select_all() {
|
|||
selection.begin = 0;
|
||||
selection.end = text.length();
|
||||
selection.enabled = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -2172,6 +2393,7 @@ void LineEdit::set_editable(bool p_editable) {
|
|||
_validate_caret_can_draw();
|
||||
|
||||
update_minimum_size();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -2241,6 +2463,7 @@ void LineEdit::select(int p_from, int p_to) {
|
|||
selection.end = p_to;
|
||||
selection.creating = false;
|
||||
selection.double_click = false;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -2611,7 +2834,7 @@ void LineEdit::_shape() {
|
|||
t = s.repeat(text.length() + ime_text.length());
|
||||
} else {
|
||||
if (!ime_text.is_empty()) {
|
||||
t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length());
|
||||
t = text.substr(0, caret_column) + ime_text + text.substr(caret_column);
|
||||
} else {
|
||||
t = text;
|
||||
}
|
||||
|
|
@ -2634,6 +2857,13 @@ void LineEdit::_shape() {
|
|||
if ((expand_to_text_length && old_size.x != size.x) || (old_size.y != size.y)) {
|
||||
update_minimum_size();
|
||||
}
|
||||
|
||||
if (accessibility_text_root_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(accessibility_text_root_element);
|
||||
accessibility_text_root_element = RID();
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void LineEdit::_fit_to_width() {
|
||||
|
|
@ -2707,7 +2937,6 @@ Key LineEdit::_get_menu_action_accelerator(const String &p_action) {
|
|||
void LineEdit::_generate_context_menu() {
|
||||
menu = memnew(PopupMenu);
|
||||
add_child(menu, false, INTERNAL_MODE_FRONT);
|
||||
menu->force_parent_owned();
|
||||
|
||||
menu_dir = memnew(PopupMenu);
|
||||
menu_dir->add_radio_check_item(ETR("Same as Layout Direction"), MENU_DIR_INHERITED);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LINE_EDIT_H
|
||||
#define LINE_EDIT_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
|
@ -93,6 +92,14 @@ private:
|
|||
bool pass = false;
|
||||
bool text_changed_dirty = false;
|
||||
|
||||
enum AltInputMode {
|
||||
ALT_INPUT_NONE,
|
||||
ALT_INPUT_UNICODE,
|
||||
ALT_INPUT_OEM,
|
||||
ALT_INPUT_WIN,
|
||||
};
|
||||
|
||||
AltInputMode alt_mode = ALT_INPUT_NONE;
|
||||
bool alt_start = false;
|
||||
bool alt_start_no_hold = false;
|
||||
uint32_t alt_code = 0;
|
||||
|
|
@ -106,6 +113,7 @@ private:
|
|||
Point2 ime_selection;
|
||||
|
||||
RID text_rid;
|
||||
RID accessibility_text_root_element;
|
||||
float full_width = 0.0;
|
||||
|
||||
bool selecting_enabled = true;
|
||||
|
|
@ -265,6 +273,11 @@ protected:
|
|||
virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
void _accessibility_action_set_selection(const Variant &p_data);
|
||||
void _accessibility_action_replace_selected(const Variant &p_data);
|
||||
void _accessibility_action_set_value(const Variant &p_data);
|
||||
void _accessibility_action_menu(const Variant &p_data);
|
||||
|
||||
public:
|
||||
void edit();
|
||||
void unedit();
|
||||
|
|
@ -419,5 +432,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(LineEdit::MenuItems);
|
||||
VARIANT_ENUM_CAST(LineEdit::VirtualKeyboardType);
|
||||
|
||||
#endif // LINE_EDIT_H
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ void LinkButton::_shape() {
|
|||
}
|
||||
TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, xl_text));
|
||||
text_buf->add_string(xl_text, font, font_size, language);
|
||||
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void LinkButton::set_text(const String &p_text) {
|
||||
|
|
@ -109,7 +111,10 @@ String LinkButton::get_language() const {
|
|||
}
|
||||
|
||||
void LinkButton::set_uri(const String &p_uri) {
|
||||
uri = p_uri;
|
||||
if (uri != p_uri) {
|
||||
uri = p_uri;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
}
|
||||
|
||||
String LinkButton::get_uri() const {
|
||||
|
|
@ -147,6 +152,17 @@ Size2 LinkButton::get_minimum_size() const {
|
|||
|
||||
void LinkButton::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_LINK);
|
||||
if (!xl_text.is_empty() && get_accessibility_name().is_empty()) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(ae, xl_text);
|
||||
}
|
||||
DisplayServer::get_singleton()->accessibility_update_set_url(ae, uri);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
xl_text = atr(text);
|
||||
_shape();
|
||||
|
|
@ -288,7 +304,7 @@ void LinkButton::_bind_methods() {
|
|||
|
||||
LinkButton::LinkButton(const String &p_text) {
|
||||
text_buf.instantiate();
|
||||
set_focus_mode(FOCUS_NONE);
|
||||
set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
set_default_cursor_shape(CURSOR_POINTING_HAND);
|
||||
|
||||
set_text(p_text);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef LINK_BUTTON_H
|
||||
#define LINK_BUTTON_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/base_button.h"
|
||||
#include "scene/resources/text_line.h"
|
||||
|
|
@ -110,5 +109,3 @@ public:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(LinkButton::UnderlineMode);
|
||||
|
||||
#endif // LINK_BUTTON_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef MARGIN_CONTAINER_H
|
||||
#define MARGIN_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -57,5 +56,3 @@ public:
|
|||
|
||||
MarginContainer();
|
||||
};
|
||||
|
||||
#endif // MARGIN_CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -85,6 +85,15 @@ void MenuBar::gui_input(const Ref<InputEvent> &p_event) {
|
|||
_open_popup(selected_menu, true);
|
||||
}
|
||||
return;
|
||||
} else if (p_event->is_action("ui_accept", true) && p_event->is_pressed()) {
|
||||
if (focused_menu == -1) {
|
||||
focused_menu = 0;
|
||||
}
|
||||
selected_menu = focused_menu;
|
||||
if (active_menu >= 0) {
|
||||
get_menu_popup(active_menu)->hide();
|
||||
}
|
||||
_open_popup(selected_menu, true);
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm = p_event;
|
||||
|
|
@ -219,9 +228,9 @@ void MenuBar::bind_global_menu() {
|
|||
String prev_tag;
|
||||
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);
|
||||
String tag = nmenu->get_item_tag(main_menu, i).operator String().get_slicec('#', 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()))));
|
||||
MenuBar *mb = ObjectDB::get_instance<MenuBar>(ObjectID(static_cast<uint64_t>(tag.to_int())));
|
||||
if (mb && mb->get_start_index() >= start_index) {
|
||||
global_start_idx = i;
|
||||
break;
|
||||
|
|
@ -276,6 +285,12 @@ void MenuBar::unbind_global_menu() {
|
|||
|
||||
void MenuBar::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_MENU_BAR);
|
||||
} break;
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
if (get_menu_count() > 0) {
|
||||
_refresh_menu_names();
|
||||
|
|
@ -408,6 +423,10 @@ void MenuBar::_draw_menu_item(int p_index) {
|
|||
bool pressed = (active_menu == p_index);
|
||||
bool rtl = is_layout_rtl();
|
||||
|
||||
if (has_focus() && focused_menu == -1 && p_index == 0) {
|
||||
hovered = true;
|
||||
}
|
||||
|
||||
if (menu_cache[p_index].hidden) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -548,7 +567,7 @@ int MenuBar::get_menu_idx_from_control(PopupMenu *p_child) const {
|
|||
}
|
||||
|
||||
void MenuBar::_popup_changed(ObjectID p_menu) {
|
||||
PopupMenu *pm = Object::cast_to<PopupMenu>(ObjectDB::get_instance(p_menu));
|
||||
PopupMenu *pm = ObjectDB::get_instance<PopupMenu>(p_menu);
|
||||
if (!pm) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -950,6 +969,7 @@ String MenuBar::get_tooltip(const Point2 &p_pos) const {
|
|||
}
|
||||
|
||||
MenuBar::MenuBar() {
|
||||
set_focus_mode(FOCUS_ALL);
|
||||
set_process_shortcut_input(true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef MENU_BAR_H
|
||||
#define MENU_BAR_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
||||
|
|
@ -195,5 +194,3 @@ public:
|
|||
MenuBar();
|
||||
~MenuBar();
|
||||
};
|
||||
|
||||
#endif // MENU_BAR_H
|
||||
|
|
|
|||
|
|
@ -126,6 +126,14 @@ int MenuButton::get_item_count() const {
|
|||
|
||||
void MenuButton::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_BUTTON);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_popup_type(ae, DisplayServer::AccessibilityPopupType::POPUP_MENU);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
|
||||
popup->set_layout_direction((Window::LayoutDirection)get_layout_direction());
|
||||
} break;
|
||||
|
|
@ -218,7 +226,7 @@ MenuButton::MenuButton(const String &p_text) :
|
|||
set_toggle_mode(true);
|
||||
set_disable_shortcuts(false);
|
||||
set_process_shortcut_input(true);
|
||||
set_focus_mode(FOCUS_NONE);
|
||||
set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
set_action_mode(ACTION_MODE_BUTTON_PRESS);
|
||||
|
||||
popup = memnew(PopupMenu);
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef MENU_BUTTON_H
|
||||
#define MENU_BUTTON_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
|
@ -78,5 +77,3 @@ public:
|
|||
MenuButton(const String &p_text = String());
|
||||
~MenuButton();
|
||||
};
|
||||
|
||||
#endif // MENU_BUTTON_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef NINE_PATCH_RECT_H
|
||||
#define NINE_PATCH_RECT_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
|
|
@ -82,5 +81,3 @@ public:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(NinePatchRect::AxisStretchMode)
|
||||
|
||||
#endif // NINE_PATCH_RECT_H
|
||||
|
|
|
|||
|
|
@ -73,6 +73,14 @@ Size2 OptionButton::get_minimum_size() const {
|
|||
|
||||
void OptionButton::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_BUTTON);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_popup_type(ae, DisplayServer::AccessibilityPopupType::POPUP_LIST);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_POSTINITIALIZE: {
|
||||
_refresh_size_cache();
|
||||
if (has_theme_icon(SNAME("arrow"))) {
|
||||
|
|
@ -165,7 +173,7 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) {
|
|||
_select(index, false);
|
||||
}
|
||||
|
||||
const String property = sname.get_slice("/", 2);
|
||||
const String property = sname.get_slicec('/', 2);
|
||||
if (property == "text" || property == "icon") {
|
||||
_queue_update_size_cache();
|
||||
}
|
||||
|
|
@ -240,6 +248,21 @@ void OptionButton::set_item_tooltip(int p_idx, const String &p_tooltip) {
|
|||
popup->set_item_tooltip(p_idx, p_tooltip);
|
||||
}
|
||||
|
||||
void OptionButton::set_item_auto_translate_mode(int p_idx, AutoTranslateMode p_mode) {
|
||||
if (p_idx < 0) {
|
||||
p_idx += get_item_count();
|
||||
}
|
||||
if (popup->get_item_auto_translate_mode(p_idx) == p_mode) {
|
||||
return;
|
||||
}
|
||||
popup->set_item_auto_translate_mode(p_idx, p_mode);
|
||||
|
||||
if (current == p_idx) {
|
||||
set_text(popup->get_item_text(p_idx));
|
||||
}
|
||||
_queue_update_size_cache();
|
||||
}
|
||||
|
||||
void OptionButton::set_item_disabled(int p_idx, bool p_disabled) {
|
||||
popup->set_item_disabled(p_idx, p_disabled);
|
||||
}
|
||||
|
|
@ -272,6 +295,10 @@ String OptionButton::get_item_tooltip(int p_idx) const {
|
|||
return popup->get_item_tooltip(p_idx);
|
||||
}
|
||||
|
||||
Node::AutoTranslateMode OptionButton::get_item_auto_translate_mode(int p_idx) const {
|
||||
return popup->get_item_auto_translate_mode(p_idx);
|
||||
}
|
||||
|
||||
bool OptionButton::is_item_disabled(int p_idx) const {
|
||||
return popup->is_item_disabled(p_idx);
|
||||
}
|
||||
|
|
@ -431,6 +458,25 @@ void OptionButton::_queue_update_size_cache() {
|
|||
callable_mp(this, &OptionButton::_refresh_size_cache).call_deferred();
|
||||
}
|
||||
|
||||
String OptionButton::_get_translated_text(const String &p_text) const {
|
||||
if (0 <= current && current < popup->get_item_count()) {
|
||||
AutoTranslateMode mode = popup->get_item_auto_translate_mode(current);
|
||||
switch (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(mode));
|
||||
}
|
||||
return atr(p_text);
|
||||
}
|
||||
|
||||
void OptionButton::select(int p_idx) {
|
||||
_select(p_idx, false);
|
||||
}
|
||||
|
|
@ -510,12 +556,14 @@ void OptionButton::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_item_id", "idx", "id"), &OptionButton::set_item_id);
|
||||
ClassDB::bind_method(D_METHOD("set_item_metadata", "idx", "metadata"), &OptionButton::set_item_metadata);
|
||||
ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &OptionButton::set_item_tooltip);
|
||||
ClassDB::bind_method(D_METHOD("set_item_auto_translate_mode", "idx", "mode"), &OptionButton::set_item_auto_translate_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_item_text", "idx"), &OptionButton::get_item_text);
|
||||
ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &OptionButton::get_item_icon);
|
||||
ClassDB::bind_method(D_METHOD("get_item_id", "idx"), &OptionButton::get_item_id);
|
||||
ClassDB::bind_method(D_METHOD("get_item_index", "id"), &OptionButton::get_item_index);
|
||||
ClassDB::bind_method(D_METHOD("get_item_metadata", "idx"), &OptionButton::get_item_metadata);
|
||||
ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &OptionButton::get_item_tooltip);
|
||||
ClassDB::bind_method(D_METHOD("get_item_auto_translate_mode", "idx"), &OptionButton::get_item_auto_translate_mode);
|
||||
ClassDB::bind_method(D_METHOD("is_item_disabled", "idx"), &OptionButton::is_item_disabled);
|
||||
ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &OptionButton::is_item_separator);
|
||||
ClassDB::bind_method(D_METHOD("add_separator", "text"), &OptionButton::add_separator, DEFVAL(String()));
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef OPTION_BUTTON_H
|
||||
#define OPTION_BUTTON_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
|
@ -80,6 +79,7 @@ class OptionButton : public Button {
|
|||
protected:
|
||||
Size2 get_minimum_size() const override;
|
||||
virtual void _queue_update_size_cache() override;
|
||||
virtual String _get_translated_text(const String &p_text) const override;
|
||||
|
||||
void _notification(int p_what);
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
|
|
@ -106,6 +106,7 @@ public:
|
|||
void set_item_metadata(int p_idx, const Variant &p_metadata);
|
||||
void set_item_disabled(int p_idx, bool p_disabled);
|
||||
void set_item_tooltip(int p_idx, const String &p_tooltip);
|
||||
void set_item_auto_translate_mode(int p_idx, AutoTranslateMode p_mode);
|
||||
|
||||
String get_item_text(int p_idx) const;
|
||||
Ref<Texture2D> get_item_icon(int p_idx) const;
|
||||
|
|
@ -115,6 +116,7 @@ public:
|
|||
bool is_item_disabled(int p_idx) const;
|
||||
bool is_item_separator(int p_idx) const;
|
||||
String get_item_tooltip(int p_idx) const;
|
||||
AutoTranslateMode get_item_auto_translate_mode(int p_idx) const;
|
||||
|
||||
bool has_selectable_items() const;
|
||||
int get_selectable_item(bool p_from_last = false) const;
|
||||
|
|
@ -150,5 +152,3 @@ public:
|
|||
OptionButton(const String &p_text = String());
|
||||
~OptionButton();
|
||||
};
|
||||
|
||||
#endif // OPTION_BUTTON_H
|
||||
|
|
|
|||
|
|
@ -33,6 +33,13 @@
|
|||
|
||||
void Panel::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_PANEL);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
RID ci = get_canvas_item();
|
||||
theme_cache.panel_style->draw(ci, Rect2(Point2(), get_size()));
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef PANEL_H
|
||||
#define PANEL_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
|
|
@ -47,5 +46,3 @@ protected:
|
|||
public:
|
||||
Panel();
|
||||
};
|
||||
|
||||
#endif // PANEL_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef PANEL_CONTAINER_H
|
||||
#define PANEL_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -52,5 +51,3 @@ public:
|
|||
|
||||
PanelContainer();
|
||||
};
|
||||
|
||||
#endif // PANEL_CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
#include "scene/theme/theme_db.h"
|
||||
|
||||
void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
|
||||
if (get_flag(FLAG_POPUP) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
|
||||
if ((ac_popup || get_flag(FLAG_POPUP)) && p_event->is_action_pressed(SNAME("ui_cancel"), false, true)) {
|
||||
hide_reason = HIDE_REASON_CANCELED; // ESC pressed, mark as canceled unconditionally.
|
||||
_close_pressed();
|
||||
}
|
||||
|
|
@ -115,7 +115,7 @@ void Popup::_notification(int p_what) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
||||
if (!is_in_edited_scene_root() && get_flag(FLAG_POPUP)) {
|
||||
if (!is_in_edited_scene_root() && (get_flag(FLAG_POPUP) || ac_popup)) {
|
||||
if (hide_reason == HIDE_REASON_NONE) {
|
||||
hide_reason = HIDE_REASON_UNFOCUSED;
|
||||
}
|
||||
|
|
@ -126,7 +126,7 @@ void Popup::_notification(int p_what) {
|
|||
}
|
||||
|
||||
void Popup::_parent_focused() {
|
||||
if (popped_up && get_flag(FLAG_POPUP)) {
|
||||
if (popped_up && (get_flag(FLAG_POPUP) || ac_popup)) {
|
||||
if (hide_reason == HIDE_REASON_NONE) {
|
||||
hide_reason = HIDE_REASON_UNFOCUSED;
|
||||
}
|
||||
|
|
@ -167,6 +167,11 @@ Rect2i Popup::_popup_adjust_rect() const {
|
|||
|
||||
Rect2i current(get_position(), get_size());
|
||||
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SELF_FITTING_WINDOWS)) {
|
||||
// We're fine as is, the Display Server will take care of that for us.
|
||||
return current;
|
||||
}
|
||||
|
||||
if (current.position.x + current.size.x > parent_rect.position.x + parent_rect.size.x) {
|
||||
current.position.x = parent_rect.position.x + parent_rect.size.x - current.size.x;
|
||||
}
|
||||
|
|
@ -218,7 +223,10 @@ Popup::Popup() {
|
|||
set_transient(true);
|
||||
set_flag(FLAG_BORDERLESS, true);
|
||||
set_flag(FLAG_RESIZE_DISABLED, true);
|
||||
set_flag(FLAG_MINIMIZE_DISABLED, true);
|
||||
set_flag(FLAG_MAXIMIZE_DISABLED, true);
|
||||
set_flag(FLAG_POPUP, true);
|
||||
set_flag(FLAG_POPUP_WM_HINT, true);
|
||||
}
|
||||
|
||||
Popup::~Popup() {
|
||||
|
|
@ -299,7 +307,7 @@ Rect2i PopupPanel::_popup_adjust_rect() const {
|
|||
_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();
|
||||
current.position -= Vector2(Math::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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef POPUP_H
|
||||
#define POPUP_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/window.h"
|
||||
|
||||
|
|
@ -41,6 +40,7 @@ class Popup : public Window {
|
|||
GDCLASS(Popup, Window);
|
||||
|
||||
LocalVector<Window *> visible_parents;
|
||||
bool ac_popup = false;
|
||||
bool popped_up = false;
|
||||
|
||||
public:
|
||||
|
|
@ -60,6 +60,7 @@ protected:
|
|||
void _close_pressed();
|
||||
virtual Rect2i _popup_adjust_rect() const override;
|
||||
virtual void _input_from_window(const Ref<InputEvent> &p_event) override;
|
||||
void set_ac_popup() { ac_popup = true; }
|
||||
|
||||
void _notification(int p_what);
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
|
@ -107,5 +108,3 @@ public:
|
|||
|
||||
PopupPanel();
|
||||
};
|
||||
|
||||
#endif // POPUP_H
|
||||
|
|
|
|||
|
|
@ -316,32 +316,26 @@ 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();
|
||||
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;
|
||||
// Transform to scroll_container local coordinates.
|
||||
const Point2 scaled_pos = p_over / get_content_scale_factor();
|
||||
const Point2 over_scroll_container =
|
||||
scroll_container->get_global_transform_with_canvas().xform_inv(scaled_pos);
|
||||
|
||||
if (!item_clickable_area.has_point(p_over)) {
|
||||
// Check if point is inside the item control as clipped by scroll_container.
|
||||
const Rect2 scroll_container_rect = Rect2(Point2(), scroll_container->get_size());
|
||||
const Rect2 bounding_rect = scroll_container_rect.intersection(control->get_rect());
|
||||
if (!bounding_rect.has_point(over_scroll_container)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
float ofs = item_clickable_area.position.y + (float)theme_cache.v_separation * win_scale * 0.5;
|
||||
// Perform item hit check in control node local space,
|
||||
// so we don't need to worry about any of the container theming.
|
||||
const float over_control_y = control->get_transform().xform_inv(over_scroll_container).y;
|
||||
float bottom_edge = 0;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
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) {
|
||||
bottom_edge += theme_cache.v_separation;
|
||||
bottom_edge += _get_item_height(i);
|
||||
if (bottom_edge > over_control_y) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
@ -499,9 +493,11 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
bool match_found = false;
|
||||
for (int i = search_from; i < items.size(); i++) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
prev_mouse_over = mouse_over;
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
match_found = true;
|
||||
|
|
@ -513,9 +509,11 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
// If the last item is not selectable, try re-searching from the start.
|
||||
for (int i = 0; i < search_from; i++) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
prev_mouse_over = mouse_over;
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
break;
|
||||
|
|
@ -537,9 +535,11 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
bool match_found = false;
|
||||
for (int i = search_from; i >= 0; i--) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
prev_mouse_over = mouse_over;
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
match_found = true;
|
||||
|
|
@ -551,9 +551,11 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
// If the first item is not selectable, try re-searching from the end.
|
||||
for (int i = items.size() - 1; i >= search_from; i--) {
|
||||
if (!items[i].separator && !items[i].disabled) {
|
||||
prev_mouse_over = mouse_over;
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
break;
|
||||
|
|
@ -738,9 +740,11 @@ void PopupMenu::_input_from_window_internal(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
|
||||
if (items[i].text.findn(search_string) == 0) {
|
||||
prev_mouse_over = mouse_over;
|
||||
mouse_over = i;
|
||||
emit_signal(SNAME("id_focused"), items[i].id);
|
||||
scroll_to_item(i);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
set_input_as_handled();
|
||||
break;
|
||||
|
|
@ -755,6 +759,7 @@ void PopupMenu::_mouse_over_update(const Point2 &p_over) {
|
|||
|
||||
if (id < 0) {
|
||||
mouse_over = -1;
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
return;
|
||||
}
|
||||
|
|
@ -766,6 +771,7 @@ void PopupMenu::_mouse_over_update(const Point2 &p_over) {
|
|||
|
||||
if (over != mouse_over) {
|
||||
mouse_over = over;
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -1060,7 +1066,7 @@ Rect2i PopupMenu::_popup_adjust_rect() const {
|
|||
_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();
|
||||
current.position -= Vector2(Math::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();
|
||||
}
|
||||
|
|
@ -1104,8 +1110,105 @@ void PopupMenu::remove_child_notify(Node *p_child) {
|
|||
_menu_changed();
|
||||
}
|
||||
|
||||
void PopupMenu::_accessibility_action_click(const Variant &p_data, int p_idx) {
|
||||
activate_item(p_idx);
|
||||
}
|
||||
|
||||
RID PopupMenu::get_focused_accessibility_element() const {
|
||||
if (mouse_over == -1) {
|
||||
return get_accessibility_element();
|
||||
} else {
|
||||
const Item &item = items[mouse_over];
|
||||
return item.accessibility_item_element;
|
||||
}
|
||||
}
|
||||
|
||||
void PopupMenu::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (system_menu_id != NativeMenu::INVALID_MENU_ID) {
|
||||
unbind_global_menu();
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
items.write[i].accessibility_item_element = RID();
|
||||
}
|
||||
accessibility_scroll_element = RID();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
if (has_meta("_menu_name")) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(ae, get_meta("_menu_name", get_name()));
|
||||
}
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_MENU);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_count(ae, items.size());
|
||||
|
||||
if (accessibility_scroll_element.is_null()) {
|
||||
accessibility_scroll_element = DisplayServer::get_singleton()->accessibility_create_sub_element(ae, DisplayServer::AccessibilityRole::ROLE_CONTAINER);
|
||||
}
|
||||
|
||||
Transform2D scroll_xform;
|
||||
scroll_xform.set_origin(Vector2i(0, -scroll_container->get_v_scroll_bar()->get_value()));
|
||||
DisplayServer::get_singleton()->accessibility_update_set_transform(accessibility_scroll_element, scroll_xform);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_bounds(accessibility_scroll_element, Rect2(0, 0, get_size().x, scroll_container->get_v_scroll_bar()->get_max()));
|
||||
|
||||
float scroll_width = scroll_container->get_v_scroll_bar()->is_visible_in_tree() ? scroll_container->get_v_scroll_bar()->get_size().width : 0;
|
||||
float display_width = control->get_size().width - scroll_width;
|
||||
Point2 ofs;
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
const Item &item = items.write[i];
|
||||
|
||||
ofs.y += i > 0 ? theme_cache.v_separation : (float)theme_cache.v_separation / 2;
|
||||
|
||||
Point2 item_ofs = ofs;
|
||||
if (item.accessibility_item_element.is_null()) {
|
||||
item.accessibility_item_element = DisplayServer::get_singleton()->accessibility_create_sub_element(accessibility_scroll_element, DisplayServer::AccessibilityRole::ROLE_MENU_ITEM);
|
||||
item.accessibility_item_dirty = true;
|
||||
}
|
||||
|
||||
item_ofs.x += item.indent * theme_cache.indent;
|
||||
float h = _get_item_height(i);
|
||||
|
||||
if (item.accessibility_item_dirty || i == prev_mouse_over || i == mouse_over) {
|
||||
switch (item.checkable_type) {
|
||||
case Item::CHECKABLE_TYPE_NONE: {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(item.accessibility_item_element, DisplayServer::AccessibilityRole::ROLE_MENU_ITEM);
|
||||
} break;
|
||||
case Item::CHECKABLE_TYPE_CHECK_BOX: {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(item.accessibility_item_element, DisplayServer::AccessibilityRole::ROLE_MENU_ITEM_CHECK_BOX);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_checked(item.accessibility_item_element, item.checked);
|
||||
} break;
|
||||
case Item::CHECKABLE_TYPE_RADIO_BUTTON: {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(item.accessibility_item_element, DisplayServer::AccessibilityRole::ROLE_MENU_ITEM_RADIO);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_checked(item.accessibility_item_element, item.checked);
|
||||
} break;
|
||||
}
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(item.accessibility_item_element, DisplayServer::AccessibilityAction::ACTION_CLICK, callable_mp(this, &PopupMenu::_accessibility_action_click).bind(i));
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_index(item.accessibility_item_element, i);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_level(item.accessibility_item_element, 0);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_selected(item.accessibility_item_element, i == mouse_over);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(item.accessibility_item_element, item.xl_text);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(item.accessibility_item_element, DisplayServer::AccessibilityFlags::FLAG_DISABLED, item.disabled);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_tooltip(item.accessibility_item_element, item.tooltip);
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_bounds(item.accessibility_item_element, Rect2(item_ofs, Size2(display_width, h + theme_cache.v_separation)));
|
||||
|
||||
item.accessibility_item_dirty = false;
|
||||
}
|
||||
ofs.y += h;
|
||||
}
|
||||
prev_mouse_over = -1;
|
||||
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent());
|
||||
if (pm) {
|
||||
|
|
@ -1118,12 +1221,6 @@ void PopupMenu::_notification(int p_what) {
|
|||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (system_menu_id != NativeMenu::INVALID_MENU_ID) {
|
||||
unbind_global_menu();
|
||||
}
|
||||
} break;
|
||||
|
||||
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
panel->add_theme_style_override(SceneStringName(panel), theme_cache.panel_style);
|
||||
|
|
@ -1146,12 +1243,14 @@ void PopupMenu::_notification(int p_what) {
|
|||
}
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
Item &item = items.write[i];
|
||||
item.xl_text = atr(item.text);
|
||||
item.xl_text = _atr(i, item.text);
|
||||
item.dirty = true;
|
||||
if (is_global) {
|
||||
nmenu->set_item_text(global_menu, i, item.xl_text);
|
||||
}
|
||||
item.accessibility_item_dirty = true;
|
||||
_shape_item(i);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1166,6 +1265,7 @@ void PopupMenu::_notification(int p_what) {
|
|||
case NOTIFICATION_WM_MOUSE_EXIT: {
|
||||
if (mouse_over >= 0 && (!items[mouse_over].submenu || submenu_over != -1)) {
|
||||
mouse_over = -1;
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
}
|
||||
} break;
|
||||
|
|
@ -1281,7 +1381,9 @@ void PopupMenu::_notification(int p_what) {
|
|||
case NOTIFICATION_VISIBILITY_CHANGED: {
|
||||
if (!is_visible()) {
|
||||
if (mouse_over >= 0) {
|
||||
prev_mouse_over = mouse_over;
|
||||
mouse_over = -1;
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -1341,6 +1443,7 @@ void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) {
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1364,6 +1467,7 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1387,6 +1491,7 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) {
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1412,6 +1517,7 @@ void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String &
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1435,6 +1541,7 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_acce
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1460,6 +1567,7 @@ void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const St
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1485,6 +1593,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1522,6 +1631,7 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1551,6 +1661,7 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1580,6 +1691,7 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1611,6 +1723,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref<
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1640,6 +1753,7 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1671,6 +1785,7 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1697,6 +1812,7 @@ void PopupMenu::add_submenu_node_item(const String &p_label, PopupMenu *p_submen
|
|||
item.text = p_label;
|
||||
item.xl_text = atr(p_label);
|
||||
item.id = p_id == -1 ? items.size() : p_id;
|
||||
item.accessibility_item_dirty = true;
|
||||
item.submenu = p_submenu;
|
||||
item.submenu_name = p_submenu->get_name();
|
||||
items.push_back(item);
|
||||
|
|
@ -1710,6 +1826,7 @@ void PopupMenu::add_submenu_node_item(const String &p_label, PopupMenu *p_submen
|
|||
}
|
||||
|
||||
_shape_item(items.size() - 1);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
|
|
@ -1731,15 +1848,18 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) {
|
|||
return;
|
||||
}
|
||||
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);
|
||||
items.write[p_idx].dirty = true;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_text(global_menu, p_idx, items[p_idx].xl_text);
|
||||
}
|
||||
_shape_item(p_idx);
|
||||
|
||||
_shape_item(p_idx);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
|
||||
child_controls_changed();
|
||||
_menu_changed();
|
||||
}
|
||||
|
|
@ -1750,9 +1870,14 @@ void PopupMenu::set_item_text_direction(int p_idx, Control::TextDirection p_text
|
|||
}
|
||||
ERR_FAIL_INDEX(p_idx, items.size());
|
||||
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
|
||||
|
||||
if (items[p_idx].text_direction != p_text_direction) {
|
||||
items.write[p_idx].text_direction = p_text_direction;
|
||||
items.write[p_idx].dirty = true;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
_shape_item(p_idx);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -1765,10 +1890,28 @@ void PopupMenu::set_item_language(int p_idx, const String &p_language) {
|
|||
if (items[p_idx].language != p_language) {
|
||||
items.write[p_idx].language = p_language;
|
||||
items.write[p_idx].dirty = true;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
_shape_item(p_idx);
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void PopupMenu::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) {
|
||||
return;
|
||||
}
|
||||
items.write[p_idx].auto_translate_mode = p_mode;
|
||||
items.write[p_idx].xl_text = _atr(p_idx, items[p_idx].text);
|
||||
items.write[p_idx].dirty = true;
|
||||
control->queue_redraw();
|
||||
}
|
||||
|
||||
void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
|
||||
if (p_idx < 0) {
|
||||
p_idx += get_item_count();
|
||||
|
|
@ -1832,11 +1975,13 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
|
|||
}
|
||||
|
||||
items.write[p_idx].checked = p_checked;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_checked(global_menu, p_idx, p_checked);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
child_controls_changed();
|
||||
_menu_changed();
|
||||
|
|
@ -1875,11 +2020,13 @@ void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) {
|
|||
|
||||
items.write[p_idx].accel = p_accel;
|
||||
items.write[p_idx].dirty = true;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_accelerator(global_menu, p_idx, p_accel);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
child_controls_changed();
|
||||
_menu_changed();
|
||||
|
|
@ -1911,11 +2058,13 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
|
|||
}
|
||||
|
||||
items.write[p_idx].disabled = p_disabled;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_disabled(global_menu, p_idx, p_disabled);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
child_controls_changed();
|
||||
_menu_changed();
|
||||
|
|
@ -1979,11 +2128,13 @@ void PopupMenu::set_item_submenu_node(int p_idx, PopupMenu *p_submenu) {
|
|||
void PopupMenu::toggle_item_checked(int p_idx) {
|
||||
ERR_FAIL_INDEX(p_idx, items.size());
|
||||
items.write[p_idx].checked = !items[p_idx].checked;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_checked(global_menu, p_idx, items[p_idx].checked);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
child_controls_changed();
|
||||
_menu_changed();
|
||||
|
|
@ -2009,6 +2160,11 @@ String PopupMenu::get_item_language(int p_idx) const {
|
|||
return items[p_idx].language;
|
||||
}
|
||||
|
||||
Node::AutoTranslateMode PopupMenu::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;
|
||||
}
|
||||
|
||||
int PopupMenu::get_item_idx_from_text(const String &text) const {
|
||||
for (int idx = 0; idx < items.size(); idx++) {
|
||||
if (items[idx].text == text) {
|
||||
|
|
@ -2115,6 +2271,9 @@ void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) {
|
|||
}
|
||||
|
||||
items.write[p_idx].separator = p_separator;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -2135,11 +2294,13 @@ void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) {
|
|||
}
|
||||
|
||||
items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_checkable(global_menu, p_idx, p_checkable);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
_menu_changed();
|
||||
}
|
||||
|
|
@ -2156,11 +2317,13 @@ void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) {
|
|||
}
|
||||
|
||||
items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_radio_checkable(global_menu, p_idx, p_radio_checkable);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
_menu_changed();
|
||||
}
|
||||
|
|
@ -2176,11 +2339,13 @@ void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) {
|
|||
}
|
||||
|
||||
items.write[p_idx].tooltip = p_tooltip;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_tooltip(global_menu, p_idx, p_tooltip);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
_menu_changed();
|
||||
}
|
||||
|
|
@ -2280,11 +2445,13 @@ void PopupMenu::set_item_multistate(int p_idx, int p_state) {
|
|||
}
|
||||
|
||||
items.write[p_idx].state = p_state;
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_state(global_menu, p_idx, p_state);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
_menu_changed();
|
||||
}
|
||||
|
|
@ -2329,11 +2496,13 @@ void PopupMenu::toggle_item_multistate(int p_idx) {
|
|||
if (items.write[p_idx].max_states <= items[p_idx].state) {
|
||||
items.write[p_idx].state = 0;
|
||||
}
|
||||
items.write[p_idx].accessibility_item_dirty = true;
|
||||
|
||||
if (global_menu.is_valid()) {
|
||||
NativeMenu::get_singleton()->set_item_state(global_menu, p_idx, items[p_idx].state);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
_menu_changed();
|
||||
}
|
||||
|
|
@ -2367,11 +2536,12 @@ void PopupMenu::set_focused_item(int p_idx) {
|
|||
return;
|
||||
}
|
||||
|
||||
prev_mouse_over = mouse_over;
|
||||
mouse_over = p_idx;
|
||||
if (mouse_over != -1) {
|
||||
scroll_to_item(mouse_over);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -2393,6 +2563,10 @@ void PopupMenu::set_item_count(int p_count) {
|
|||
if (is_global && prev_size > p_count) {
|
||||
for (int i = prev_size - 1; i >= p_count; i--) {
|
||||
nmenu->remove_item(global_menu, i);
|
||||
if (items[i].accessibility_item_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(items.write[i].accessibility_item_element);
|
||||
items.write[i].accessibility_item_element = RID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2573,6 +2747,10 @@ void PopupMenu::activate_item(int p_idx) {
|
|||
void PopupMenu::remove_item(int p_idx) {
|
||||
ERR_FAIL_INDEX(p_idx, items.size());
|
||||
|
||||
if (items[p_idx].accessibility_item_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(items.write[p_idx].accessibility_item_element);
|
||||
items.write[p_idx].accessibility_item_element = RID();
|
||||
}
|
||||
if (items[p_idx].shortcut.is_valid()) {
|
||||
_unref_shortcut(items[p_idx].shortcut);
|
||||
}
|
||||
|
|
@ -2592,6 +2770,7 @@ void PopupMenu::add_separator(const String &p_text, int p_id) {
|
|||
Item sep;
|
||||
sep.separator = true;
|
||||
sep.id = p_id;
|
||||
sep.accessibility_item_dirty = true;
|
||||
if (!p_text.is_empty()) {
|
||||
sep.text = p_text;
|
||||
sep.xl_text = atr(p_text);
|
||||
|
|
@ -2607,7 +2786,11 @@ void PopupMenu::add_separator(const String &p_text, int p_id) {
|
|||
}
|
||||
|
||||
void PopupMenu::clear(bool p_free_submenus) {
|
||||
for (const Item &I : items) {
|
||||
for (Item &I : items) {
|
||||
if (I.accessibility_item_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(I.accessibility_item_element);
|
||||
I.accessibility_item_element = RID();
|
||||
}
|
||||
if (I.shortcut.is_valid()) {
|
||||
_unref_shortcut(I.shortcut);
|
||||
}
|
||||
|
|
@ -2631,7 +2814,9 @@ void PopupMenu::clear(bool p_free_submenus) {
|
|||
}
|
||||
items.clear();
|
||||
|
||||
prev_mouse_over = -1;
|
||||
mouse_over = -1;
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
child_controls_changed();
|
||||
notify_property_list_changed();
|
||||
|
|
@ -2818,6 +3003,7 @@ void PopupMenu::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_item_text", "index", "text"), &PopupMenu::set_item_text);
|
||||
ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction);
|
||||
ClassDB::bind_method(D_METHOD("set_item_language", "index", "language"), &PopupMenu::set_item_language);
|
||||
ClassDB::bind_method(D_METHOD("set_item_auto_translate_mode", "index", "mode"), &PopupMenu::set_item_auto_translate_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_item_icon", "index", "icon"), &PopupMenu::set_item_icon);
|
||||
ClassDB::bind_method(D_METHOD("set_item_icon_max_width", "index", "width"), &PopupMenu::set_item_icon_max_width);
|
||||
ClassDB::bind_method(D_METHOD("set_item_icon_modulate", "index", "modulate"), &PopupMenu::set_item_icon_modulate);
|
||||
|
|
@ -2844,6 +3030,7 @@ void PopupMenu::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_item_text", "index"), &PopupMenu::get_item_text);
|
||||
ClassDB::bind_method(D_METHOD("get_item_text_direction", "index"), &PopupMenu::get_item_text_direction);
|
||||
ClassDB::bind_method(D_METHOD("get_item_language", "index"), &PopupMenu::get_item_language);
|
||||
ClassDB::bind_method(D_METHOD("get_item_auto_translate_mode", "index"), &PopupMenu::get_item_auto_translate_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_item_icon", "index"), &PopupMenu::get_item_icon);
|
||||
ClassDB::bind_method(D_METHOD("get_item_icon_max_width", "index"), &PopupMenu::get_item_icon_max_width);
|
||||
ClassDB::bind_method(D_METHOD("get_item_icon_modulate", "index"), &PopupMenu::get_item_icon_modulate);
|
||||
|
|
@ -2983,6 +3170,23 @@ void PopupMenu::_native_popup(const Rect2i &p_rect) {
|
|||
NativeMenu::get_singleton()->popup(global_menu, popup_pos);
|
||||
}
|
||||
|
||||
String PopupMenu::_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));
|
||||
}
|
||||
|
||||
void PopupMenu::popup(const Rect2i &p_bounds) {
|
||||
bool native = global_menu.is_valid();
|
||||
#ifdef TOOLS_ENABLED
|
||||
|
|
@ -2994,18 +3198,30 @@ void PopupMenu::popup(const Rect2i &p_bounds) {
|
|||
if (native) {
|
||||
_native_popup(p_bounds != Rect2i() ? p_bounds : Rect2i(get_position(), Size2i()));
|
||||
} else {
|
||||
set_flag(FLAG_NO_FOCUS, !is_embedded());
|
||||
if (is_inside_tree()) {
|
||||
bool ac = get_tree()->is_accessibility_enabled();
|
||||
// Note: Native popup menus need keyboard focus to work with screen reader.
|
||||
set_flag(FLAG_POPUP, !ac);
|
||||
set_flag(FLAG_NO_FOCUS, !is_embedded() && !ac);
|
||||
if (ac) {
|
||||
set_ac_popup();
|
||||
}
|
||||
}
|
||||
|
||||
moved = Vector2();
|
||||
popup_time_msec = OS::get_singleton()->get_ticks_msec();
|
||||
if (!is_embedded()) {
|
||||
float win_scale = get_parent_visible_window()->get_content_scale_factor();
|
||||
set_content_scale_factor(win_scale);
|
||||
Size2 minsize = get_contents_minimum_size() * win_scale;
|
||||
minsize.height = Math::ceil(minsize.height); // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing.
|
||||
set_min_size(minsize); // `height` is truncated here by the cast to Size2i for Window.min_size.
|
||||
set_size(Vector2(0, 0)); // Shrinkwraps to min size.
|
||||
|
||||
Size2 scale = get_parent_viewport()->get_popup_base_transform().get_scale();
|
||||
CanvasItem *c = Object::cast_to<CanvasItem>(get_parent());
|
||||
if (c) {
|
||||
scale *= c->get_global_transform_with_canvas().get_scale();
|
||||
}
|
||||
real_t popup_scale = MIN(scale.x, scale.y);
|
||||
set_content_scale_factor(popup_scale);
|
||||
Size2 minsize = get_contents_minimum_size() * popup_scale;
|
||||
minsize.height = Math::ceil(minsize.height); // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing.
|
||||
set_min_size(minsize); // `height` is truncated here by the cast to Size2i for Window.min_size.
|
||||
set_size(Vector2(0, 0)); // Shrinkwraps to min size.
|
||||
Popup::popup(p_bounds);
|
||||
}
|
||||
}
|
||||
|
|
@ -3023,7 +3239,15 @@ void PopupMenu::set_visible(bool p_visible) {
|
|||
_native_popup(Rect2i(get_position(), get_size()));
|
||||
}
|
||||
} else {
|
||||
set_flag(FLAG_NO_FOCUS, !is_embedded());
|
||||
if (is_inside_tree()) {
|
||||
bool ac = get_tree()->is_accessibility_enabled();
|
||||
// Note: Native popup menus need keyboard focus to work with screen reader.
|
||||
set_flag(FLAG_POPUP, !ac);
|
||||
set_flag(FLAG_NO_FOCUS, !is_embedded() && !ac);
|
||||
if (ac) {
|
||||
set_ac_popup();
|
||||
}
|
||||
}
|
||||
|
||||
Popup::set_visible(p_visible);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef POPUP_MENU_H
|
||||
#define POPUP_MENU_H
|
||||
#pragma once
|
||||
|
||||
#include "core/input/shortcut.h"
|
||||
#include "scene/gui/popup.h"
|
||||
|
|
@ -45,6 +44,9 @@ class PopupMenu : public Popup {
|
|||
static HashMap<NativeMenu::SystemMenus, PopupMenu *> system_menus;
|
||||
|
||||
struct Item {
|
||||
mutable RID accessibility_item_element;
|
||||
mutable bool accessibility_item_dirty = true;
|
||||
|
||||
Ref<Texture2D> icon;
|
||||
int icon_max_width = 0;
|
||||
Color icon_modulate = Color(1, 1, 1, 1);
|
||||
|
|
@ -55,6 +57,7 @@ class PopupMenu : public Popup {
|
|||
|
||||
String language;
|
||||
Control::TextDirection text_direction = Control::TEXT_DIRECTION_AUTO;
|
||||
AutoTranslateMode auto_translate_mode = AUTO_TRANSLATE_MODE_INHERIT;
|
||||
|
||||
bool checked = false;
|
||||
enum {
|
||||
|
|
@ -95,6 +98,7 @@ class PopupMenu : public Popup {
|
|||
|
||||
Item(bool p_dummy) {}
|
||||
};
|
||||
RID accessibility_scroll_element;
|
||||
|
||||
mutable Rect2i pre_popup_rect;
|
||||
void _update_shadow_offsets() const;
|
||||
|
|
@ -118,10 +122,11 @@ class PopupMenu : public Popup {
|
|||
Timer *submenu_timer = nullptr;
|
||||
List<Rect2> autohide_areas;
|
||||
mutable Vector<Item> items;
|
||||
BitField<MouseButtonMask> initial_button_mask;
|
||||
BitField<MouseButtonMask> initial_button_mask = MouseButtonMask::NONE;
|
||||
bool during_grabbed_click = false;
|
||||
bool is_scrolling = false;
|
||||
int mouse_over = -1;
|
||||
int prev_mouse_over = -1;
|
||||
int submenu_over = -1;
|
||||
String _get_accel_text(const Item &p_item) const;
|
||||
int _get_mouse_over(const Point2 &p_over) const;
|
||||
|
|
@ -134,6 +139,8 @@ class PopupMenu : public Popup {
|
|||
|
||||
void _shape_item(int p_idx) const;
|
||||
|
||||
void _accessibility_action_click(const Variant &p_data, int p_idx);
|
||||
|
||||
void _activate_submenu(int p_over, bool p_by_keyboard = false);
|
||||
void _submenu_timeout();
|
||||
|
||||
|
|
@ -216,6 +223,7 @@ class PopupMenu : public Popup {
|
|||
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);
|
||||
String _atr(int p_idx, const String &p_text) const;
|
||||
|
||||
protected:
|
||||
virtual Rect2i _popup_adjust_rect() const override;
|
||||
|
|
@ -248,6 +256,8 @@ public:
|
|||
// this value should be updated to reflect the new size.
|
||||
static const int ITEM_PROPERTY_SIZE = 10;
|
||||
|
||||
virtual RID get_focused_accessibility_element() const override;
|
||||
|
||||
virtual void _parent_focused() override;
|
||||
|
||||
RID bind_global_menu();
|
||||
|
|
@ -280,6 +290,7 @@ public:
|
|||
|
||||
void set_item_text_direction(int p_idx, Control::TextDirection p_text_direction);
|
||||
void set_item_language(int p_idx, const String &p_language);
|
||||
void set_item_auto_translate_mode(int p_idx, AutoTranslateMode p_mode);
|
||||
void set_item_icon(int p_idx, const Ref<Texture2D> &p_icon);
|
||||
void set_item_icon_max_width(int p_idx, int p_width);
|
||||
void set_item_icon_modulate(int p_idx, const Color &p_modulate);
|
||||
|
|
@ -307,6 +318,7 @@ public:
|
|||
String get_item_xl_text(int p_idx) const;
|
||||
Control::TextDirection get_item_text_direction(int p_idx) const;
|
||||
String get_item_language(int p_idx) const;
|
||||
AutoTranslateMode get_item_auto_translate_mode(int p_idx) const;
|
||||
int get_item_idx_from_text(const String &text) const;
|
||||
Ref<Texture2D> get_item_icon(int p_idx) const;
|
||||
int get_item_icon_max_width(int p_idx) const;
|
||||
|
|
@ -385,5 +397,3 @@ public:
|
|||
PopupMenu();
|
||||
~PopupMenu();
|
||||
};
|
||||
|
||||
#endif // POPUP_MENU_H
|
||||
|
|
|
|||
|
|
@ -54,6 +54,14 @@ void ProgressBar::_notification(int p_what) {
|
|||
queue_redraw();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_PROGRESS_INDICATOR);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
draw_style_box(theme_cache.background_style, Rect2(Point2(), get_size()));
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef PROGRESS_BAR_H
|
||||
#define PROGRESS_BAR_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/range.h"
|
||||
|
||||
|
|
@ -90,5 +89,3 @@ private:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(ProgressBar::FillMode);
|
||||
|
||||
#endif // PROGRESS_BAR_H
|
||||
|
|
|
|||
|
|
@ -43,12 +43,51 @@ PackedStringArray Range::get_configuration_warnings() const {
|
|||
void Range::_value_changed(double p_value) {
|
||||
GDVIRTUAL_CALL(_value_changed, p_value);
|
||||
}
|
||||
|
||||
void Range::_value_changed_notify() {
|
||||
_value_changed(shared->val);
|
||||
emit_signal(SceneStringName(value_changed), shared->val);
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void Range::_accessibility_action_inc(const Variant &p_data) {
|
||||
double step = ((shared->step > 0) ? shared->step : 1);
|
||||
set_value(shared->val + step);
|
||||
}
|
||||
|
||||
void Range::_accessibility_action_dec(const Variant &p_data) {
|
||||
double step = ((shared->step > 0) ? shared->step : 1);
|
||||
set_value(shared->val - step);
|
||||
}
|
||||
|
||||
void Range::_accessibility_action_set_value(const Variant &p_data) {
|
||||
double new_val = p_data;
|
||||
set_value(new_val);
|
||||
}
|
||||
|
||||
void Range::_notification(int p_what) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_SPIN_BUTTON);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_num_value(ae, shared->val);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_num_range(ae, shared->min, shared->max);
|
||||
if (shared->step > 0) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_num_step(ae, shared->step);
|
||||
} else {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_num_step(ae, 1);
|
||||
}
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_DECREMENT, callable_mp(this, &Range::_accessibility_action_dec));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_INCREMENT, callable_mp(this, &Range::_accessibility_action_inc));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SET_VALUE, callable_mp(this, &Range::_accessibility_action_set_value));
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Range::Shared::emit_value_changed() {
|
||||
for (Range *E : owners) {
|
||||
Range *r = E;
|
||||
|
|
@ -80,6 +119,7 @@ void Range::Shared::redraw_owners() {
|
|||
if (!r->is_inside_tree()) {
|
||||
continue;
|
||||
}
|
||||
r->queue_accessibility_update();
|
||||
r->queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -91,6 +131,7 @@ void Range::set_value(double p_val) {
|
|||
if (shared->val != prev_val) {
|
||||
shared->emit_value_changed();
|
||||
}
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void Range::_set_value_no_signal(double p_val) {
|
||||
|
|
@ -143,6 +184,8 @@ void Range::set_min(double p_min) {
|
|||
shared->emit_changed("min");
|
||||
|
||||
update_configuration_warnings();
|
||||
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void Range::set_max(double p_max) {
|
||||
|
|
@ -156,6 +199,8 @@ void Range::set_max(double p_max) {
|
|||
set_value(shared->val);
|
||||
|
||||
shared->emit_changed("max");
|
||||
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void Range::set_step(double p_step) {
|
||||
|
|
@ -165,6 +210,8 @@ void Range::set_step(double p_step) {
|
|||
|
||||
shared->step = p_step;
|
||||
shared->emit_changed("step");
|
||||
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void Range::set_page(double p_page) {
|
||||
|
|
@ -177,6 +224,8 @@ void Range::set_page(double p_page) {
|
|||
set_value(shared->val);
|
||||
|
||||
shared->emit_changed("page");
|
||||
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
double Range::get_value() const {
|
||||
|
|
@ -264,6 +313,7 @@ void Range::unshare() {
|
|||
nshared->allow_lesser = shared->allow_lesser;
|
||||
_unref_shared();
|
||||
_ref_shared(nshared);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void Range::_ref_shared(Shared *p_shared) {
|
||||
|
|
@ -279,7 +329,7 @@ void Range::_ref_shared(Shared *p_shared) {
|
|||
void Range::_unref_shared() {
|
||||
if (shared) {
|
||||
shared->owners.erase(this);
|
||||
if (shared->owners.size() == 0) {
|
||||
if (shared->owners.is_empty()) {
|
||||
memdelete(shared);
|
||||
shared = nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RANGE_H
|
||||
#define RANGE_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
|
|
@ -65,9 +64,14 @@ class Range : public Control {
|
|||
protected:
|
||||
virtual void _value_changed(double p_value);
|
||||
void _notify_shared_value_changed() { shared->emit_value_changed(); }
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
void _accessibility_action_inc(const Variant &p_data);
|
||||
void _accessibility_action_dec(const Variant &p_data);
|
||||
void _accessibility_action_set_value(const Variant &p_data);
|
||||
|
||||
bool _rounded_values = false;
|
||||
|
||||
GDVIRTUAL1(_value_changed, double)
|
||||
|
|
@ -108,5 +112,3 @@ public:
|
|||
Range();
|
||||
~Range();
|
||||
};
|
||||
|
||||
#endif // RANGE_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef REFERENCE_RECT_H
|
||||
#define REFERENCE_RECT_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
|
|
@ -54,5 +53,3 @@ public:
|
|||
void set_editor_only(const bool &p_enabled);
|
||||
bool get_editor_only() const;
|
||||
};
|
||||
|
||||
#endif // REFERENCE_RECT_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RICH_TEXT_EFFECT_H
|
||||
#define RICH_TEXT_EFFECT_H
|
||||
#pragma once
|
||||
|
||||
#include "core/io/resource.h"
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
|
|
@ -113,5 +112,3 @@ public:
|
|||
|
||||
RichTextEffect();
|
||||
};
|
||||
|
||||
#endif // RICH_TEXT_EFFECT_H
|
||||
|
|
|
|||
|
|
@ -51,7 +51,15 @@ void RichTextLabel::_push_meta_bind_compat_89024(const Variant &p_meta) {
|
|||
}
|
||||
|
||||
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) {
|
||||
add_image(p_image, p_width, p_height, p_color, p_alignment, p_region, Variant(), false, String(), false);
|
||||
add_image(p_image, p_width, p_height, p_color, p_alignment, p_region, Variant(), false, String(), false, String());
|
||||
}
|
||||
|
||||
void RichTextLabel::_add_image_bind_compat_76829(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region, const Variant &p_key, bool p_pad, const String &p_tooltip, bool p_size_in_percent) {
|
||||
add_image(p_image, p_width, p_height, p_color, p_alignment, p_region, p_key, p_pad, p_tooltip, p_size_in_percent, String());
|
||||
}
|
||||
|
||||
void RichTextLabel::_push_table_bind_compat_76829(int p_columns, InlineAlignment p_alignment, int p_align_to_row) {
|
||||
push_table(p_columns, p_alignment, p_align_to_row, String());
|
||||
}
|
||||
|
||||
bool RichTextLabel::_remove_paragraph_bind_compat_91098(int p_paragraph) {
|
||||
|
|
@ -65,6 +73,8 @@ void RichTextLabel::_bind_compatibility_methods() {
|
|||
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("add_image", "image", "width", "height", "color", "inline_align", "region", "key", "pad", "tooltip", "size_in_percent"), &RichTextLabel::_add_image_bind_compat_76829, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2()), DEFVAL(Variant()), DEFVAL(false), DEFVAL(String()), DEFVAL(false));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("push_table", "columns", "inline_align", "align_to_row"), &RichTextLabel::_push_table_bind_compat_76829, DEFVAL(INLINE_ALIGNMENT_TOP), DEFVAL(-1));
|
||||
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
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef RICH_TEXT_LABEL_H
|
||||
#define RICH_TEXT_LABEL_H
|
||||
#pragma once
|
||||
|
||||
#include "core/object/worker_thread_pool.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
|
|
@ -45,6 +44,7 @@ class RichTextLabel : public Control {
|
|||
|
||||
enum RTLDrawStep {
|
||||
DRAW_STEP_BACKGROUND,
|
||||
DRAW_STEP_SHADOW_OUTLINE,
|
||||
DRAW_STEP_SHADOW,
|
||||
DRAW_STEP_OUTLINE,
|
||||
DRAW_STEP_TEXT,
|
||||
|
|
@ -106,12 +106,12 @@ public:
|
|||
};
|
||||
|
||||
enum DefaultFont {
|
||||
NORMAL_FONT,
|
||||
BOLD_FONT,
|
||||
ITALICS_FONT,
|
||||
BOLD_ITALICS_FONT,
|
||||
MONO_FONT,
|
||||
CUSTOM_FONT,
|
||||
RTL_NORMAL_FONT,
|
||||
RTL_BOLD_FONT,
|
||||
RTL_ITALICS_FONT,
|
||||
RTL_BOLD_ITALICS_FONT,
|
||||
RTL_MONO_FONT,
|
||||
RTL_CUSTOM_FONT,
|
||||
};
|
||||
|
||||
enum ImageUpdateMask {
|
||||
|
|
@ -137,8 +137,11 @@ protected:
|
|||
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);
|
||||
void _add_image_bind_compat_76829(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region, const Variant &p_key, bool p_pad, const String &p_tooltip, bool p_size_in_percent);
|
||||
void _push_table_bind_compat_76829(int p_columns, InlineAlignment p_alignment, int p_align_to_row);
|
||||
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
|
||||
|
||||
|
|
@ -151,6 +154,11 @@ private:
|
|||
Ref<TextLine> text_prefix;
|
||||
float prefix_width = 0;
|
||||
Ref<TextParagraph> text_buf;
|
||||
|
||||
RID accessibility_line_element;
|
||||
RID accessibility_text_element;
|
||||
|
||||
Item *dc_item = nullptr;
|
||||
Color dc_color;
|
||||
int dc_ol_size = 0;
|
||||
Color dc_ol_color;
|
||||
|
|
@ -160,7 +168,16 @@ private:
|
|||
int char_offset = 0;
|
||||
int char_count = 0;
|
||||
|
||||
Line() { text_buf.instantiate(); }
|
||||
Line() {
|
||||
text_buf.instantiate();
|
||||
}
|
||||
~Line() {
|
||||
if (accessibility_line_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(accessibility_line_element);
|
||||
accessibility_line_element = RID();
|
||||
accessibility_text_element = RID();
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ float get_height(float line_separation) const {
|
||||
return offset.y + text_buf->get_size().y + text_buf->get_line_count() * line_separation;
|
||||
|
|
@ -178,8 +195,10 @@ private:
|
|||
int line = 0;
|
||||
RID rid;
|
||||
|
||||
RID accessibility_item_element;
|
||||
|
||||
void _clear_children() {
|
||||
RichTextLabel *owner_rtl = Object::cast_to<RichTextLabel>(ObjectDB::get_instance(owner));
|
||||
RichTextLabel *owner_rtl = ObjectDB::get_instance<RichTextLabel>(owner);
|
||||
while (subitems.size()) {
|
||||
Item *subitem = subitems.front()->get();
|
||||
if (subitem && subitem->rid.is_valid() && owner_rtl) {
|
||||
|
|
@ -237,6 +256,7 @@ private:
|
|||
|
||||
struct ItemImage : public Item {
|
||||
Ref<Texture2D> image;
|
||||
String alt_text;
|
||||
InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
|
||||
bool pad = false;
|
||||
bool size_in_percent = false;
|
||||
|
|
@ -249,7 +269,7 @@ private:
|
|||
ItemImage() { type = ITEM_IMAGE; }
|
||||
~ItemImage() {
|
||||
if (image.is_valid()) {
|
||||
RichTextLabel *owner_rtl = Object::cast_to<RichTextLabel>(ObjectDB::get_instance(owner));
|
||||
RichTextLabel *owner_rtl = ObjectDB::get_instance<RichTextLabel>(owner);
|
||||
if (owner_rtl) {
|
||||
image->disconnect_changed(callable_mp(owner_rtl, &RichTextLabel::_texture_changed));
|
||||
}
|
||||
|
|
@ -258,7 +278,7 @@ private:
|
|||
};
|
||||
|
||||
struct ItemFont : public Item {
|
||||
DefaultFont def_font = CUSTOM_FONT;
|
||||
DefaultFont def_font = RTL_CUSTOM_FONT;
|
||||
Ref<Font> font;
|
||||
bool variation = false;
|
||||
bool def_size = false;
|
||||
|
|
@ -341,17 +361,21 @@ private:
|
|||
|
||||
struct ItemTable : public Item {
|
||||
struct Column {
|
||||
String name;
|
||||
bool expand = false;
|
||||
bool shrink = true;
|
||||
int expand_ratio = 0;
|
||||
int min_width = 0;
|
||||
int max_width = 0;
|
||||
int width = 0;
|
||||
int width_with_padding = 0;
|
||||
};
|
||||
|
||||
LocalVector<Column> columns;
|
||||
LocalVector<float> rows;
|
||||
LocalVector<float> rows_no_padding;
|
||||
LocalVector<float> rows_baseline;
|
||||
String name;
|
||||
|
||||
int align_to_row = -1;
|
||||
int total_width = 0;
|
||||
|
|
@ -472,6 +496,7 @@ private:
|
|||
VScrollBar *vscroll = nullptr;
|
||||
|
||||
TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_WORD_SMART;
|
||||
BitField<TextServer::LineBreakFlag> autowrap_flags_trim = TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES;
|
||||
|
||||
bool scroll_visible = false;
|
||||
bool scroll_follow = false;
|
||||
|
|
@ -500,6 +525,9 @@ private:
|
|||
|
||||
Array custom_effects;
|
||||
|
||||
HashMap<RID, Rect2> ac_element_bounds_cache;
|
||||
|
||||
void _invalidate_accessibility();
|
||||
void _invalidate_current_line(ItemFrame *p_frame);
|
||||
|
||||
void _thread_function(void *p_userdata);
|
||||
|
|
@ -549,6 +577,11 @@ private:
|
|||
bool deselect_on_focus_loss_enabled = true;
|
||||
bool drag_and_drop_selection_enabled = true;
|
||||
|
||||
ItemFrame *keyboard_focus_frame = nullptr;
|
||||
int keyboard_focus_line = 0;
|
||||
Item *keyboard_focus_item = nullptr;
|
||||
bool keyboard_focus_on_text = true;
|
||||
|
||||
bool context_menu_enabled = false;
|
||||
bool shortcut_keys_enabled = true;
|
||||
|
||||
|
|
@ -577,6 +610,7 @@ private:
|
|||
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, 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);
|
||||
void _accessibility_update_line(RID p_id, ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, float p_vsep);
|
||||
|
||||
String _roman(int p_num, bool p_capitalize) const;
|
||||
String _letters(int p_num, bool p_capitalize) const;
|
||||
|
|
@ -643,6 +677,15 @@ private:
|
|||
bool internal_stack_editing = false;
|
||||
bool stack_externally_modified = false;
|
||||
|
||||
void _accessibility_action_menu(const Variant &p_data);
|
||||
void _accessibility_scroll_down(const Variant &p_data);
|
||||
void _accessibility_scroll_up(const Variant &p_data);
|
||||
void _accessibility_scroll_set(const Variant &p_data);
|
||||
void _accessibility_focus_item(const Variant &p_data, uint64_t p_item, bool p_line, bool p_foucs);
|
||||
void _accessibility_scroll_to_item(const Variant &p_data, uint64_t p_item);
|
||||
|
||||
RID accessibility_scroll_element;
|
||||
|
||||
bool fit_content = false;
|
||||
|
||||
struct ThemeCache {
|
||||
|
|
@ -689,9 +732,12 @@ private:
|
|||
} theme_cache;
|
||||
|
||||
public:
|
||||
virtual RID get_focused_accessibility_element() const override;
|
||||
PackedStringArray get_accessibility_configuration_warnings() const override;
|
||||
|
||||
String get_parsed_text() const;
|
||||
void add_text(const String &p_text);
|
||||
void add_image(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER, const Rect2 &p_region = Rect2(), const Variant &p_key = Variant(), bool p_pad = false, const String &p_tooltip = String(), bool p_size_in_percent = false);
|
||||
void add_image(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER, const Rect2 &p_region = Rect2(), const Variant &p_key = Variant(), bool p_pad = false, const String &p_tooltip = String(), bool p_size_in_percent = false, const String &p_alt_text = String());
|
||||
void update_image(const Variant &p_key, BitField<ImageUpdateMask> p_mask, const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER, const Rect2 &p_region = Rect2(), bool p_pad = false, const String &p_tooltip = String(), bool p_size_in_percent = false);
|
||||
void add_newline();
|
||||
bool remove_paragraph(int p_paragraph, bool p_no_invalidate = false);
|
||||
|
|
@ -717,7 +763,7 @@ public:
|
|||
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, 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_table(int p_columns, InlineAlignment p_alignment = INLINE_ALIGNMENT_TOP, int p_align_to_row = -1, const String &p_name = String());
|
||||
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);
|
||||
|
|
@ -729,6 +775,7 @@ public:
|
|||
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, bool p_shrink = true);
|
||||
void set_table_column_name(int p_column, const String &p_name);
|
||||
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);
|
||||
|
|
@ -858,6 +905,9 @@ public:
|
|||
void set_autowrap_mode(TextServer::AutowrapMode p_mode);
|
||||
TextServer::AutowrapMode get_autowrap_mode() const;
|
||||
|
||||
void set_autowrap_trim_flags(BitField<TextServer::LineBreakFlag> p_flags);
|
||||
BitField<TextServer::LineBreakFlag> get_autowrap_trim_flags() const;
|
||||
|
||||
void set_structured_text_bidi_override(TextServer::StructuredTextParser p_parser);
|
||||
TextServer::StructuredTextParser get_structured_text_bidi_override() const;
|
||||
|
||||
|
|
@ -881,6 +931,7 @@ public:
|
|||
Array get_effects();
|
||||
|
||||
void install_effect(const Variant effect);
|
||||
void reload_effects();
|
||||
|
||||
virtual Size2 get_minimum_size() const override;
|
||||
|
||||
|
|
@ -892,5 +943,3 @@ VARIANT_ENUM_CAST(RichTextLabel::ListType);
|
|||
VARIANT_ENUM_CAST(RichTextLabel::MenuItems);
|
||||
VARIANT_ENUM_CAST(RichTextLabel::MetaUnderline);
|
||||
VARIANT_BITFIELD_CAST(RichTextLabel::ImageUpdateMask);
|
||||
|
||||
#endif // RICH_TEXT_LABEL_H
|
||||
|
|
|
|||
|
|
@ -224,6 +224,13 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
void ScrollBar::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_SCROLL_BAR);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
RID ci = get_canvas_item();
|
||||
|
||||
|
|
@ -654,6 +661,8 @@ ScrollBar::ScrollBar(Orientation p_orientation) {
|
|||
|
||||
if (focus_by_default) {
|
||||
set_focus_mode(FOCUS_ALL);
|
||||
} else {
|
||||
set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
}
|
||||
set_step(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SCROLL_BAR_H
|
||||
#define SCROLL_BAR_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/range.h"
|
||||
|
||||
|
|
@ -144,5 +143,3 @@ public:
|
|||
VScrollBar() :
|
||||
ScrollBar(VERTICAL) { set_h_size_flags(0); }
|
||||
};
|
||||
|
||||
#endif // SCROLL_BAR_H
|
||||
|
|
|
|||
|
|
@ -353,14 +353,53 @@ void ScrollContainer::_reposition_children() {
|
|||
queue_redraw();
|
||||
}
|
||||
|
||||
void ScrollContainer::_accessibility_action_scroll_set(const Variant &p_data) {
|
||||
const Point2 &pos = p_data;
|
||||
h_scroll->set_value(pos.x);
|
||||
v_scroll->set_value(pos.y);
|
||||
}
|
||||
|
||||
void ScrollContainer::_accessibility_action_scroll_up(const Variant &p_data) {
|
||||
v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() / 8);
|
||||
}
|
||||
|
||||
void ScrollContainer::_accessibility_action_scroll_down(const Variant &p_data) {
|
||||
v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() / 8);
|
||||
}
|
||||
|
||||
void ScrollContainer::_accessibility_action_scroll_left(const Variant &p_data) {
|
||||
h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() / 8);
|
||||
}
|
||||
|
||||
void ScrollContainer::_accessibility_action_scroll_right(const Variant &p_data) {
|
||||
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() / 8);
|
||||
}
|
||||
|
||||
void ScrollContainer::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_SCROLL_VIEW);
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_DOWN, callable_mp(this, &ScrollContainer::_accessibility_action_scroll_down));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_LEFT, callable_mp(this, &ScrollContainer::_accessibility_action_scroll_left));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_RIGHT, callable_mp(this, &ScrollContainer::_accessibility_action_scroll_right));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_UP, callable_mp(this, &ScrollContainer::_accessibility_action_scroll_up));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SET_SCROLL_OFFSET, callable_mp(this, &ScrollContainer::_accessibility_action_scroll_set));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE:
|
||||
case NOTIFICATION_THEME_CHANGED:
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
|
||||
case NOTIFICATION_TRANSLATION_CHANGED: {
|
||||
_updating_scrollbars = true;
|
||||
callable_mp(this, is_ready() ? &ScrollContainer::_reposition_children : &ScrollContainer::_update_scrollbar_position).call_deferred();
|
||||
if (p_what == NOTIFICATION_THEME_CHANGED) {
|
||||
scroll_border = get_theme_constant(SNAME("scroll_border"), SNAME("Tree"));
|
||||
scroll_speed = get_theme_constant(SNAME("scroll_speed"), SNAME("Tree"));
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_READY: {
|
||||
|
|
@ -387,6 +426,43 @@ void ScrollContainer::_notification(int p_what) {
|
|||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAG_BEGIN: {
|
||||
if (scroll_on_drag_hover && is_visible_in_tree()) {
|
||||
set_process_internal(true);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAG_END: {
|
||||
set_process_internal(false);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
if (scroll_on_drag_hover && get_viewport()->gui_is_dragging()) {
|
||||
Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position();
|
||||
Transform2D xform = get_transform();
|
||||
if (Rect2(Point2(), xform.get_scale() * get_size()).grow(scroll_border).has_point(mouse_position)) {
|
||||
Point2 point;
|
||||
|
||||
if ((Math::abs(mouse_position.x) < Math::abs(mouse_position.x - get_size().width)) && (Math::abs(mouse_position.x) < scroll_border)) {
|
||||
point.x = mouse_position.x - scroll_border;
|
||||
} else if (Math::abs(mouse_position.x - get_size().width) < scroll_border) {
|
||||
point.x = mouse_position.x - (get_size().width - scroll_border);
|
||||
}
|
||||
|
||||
if ((Math::abs(mouse_position.y) < Math::abs(mouse_position.y - get_size().height)) && (Math::abs(mouse_position.y) < scroll_border)) {
|
||||
point.y = mouse_position.y - scroll_border;
|
||||
} else if (Math::abs(mouse_position.y - get_size().height) < scroll_border) {
|
||||
point.y = mouse_position.y - (get_size().height - scroll_border);
|
||||
}
|
||||
|
||||
point *= scroll_speed * get_process_delta_time();
|
||||
point += Point2(get_h_scroll(), get_v_scroll());
|
||||
h_scroll->set_value(point.x);
|
||||
v_scroll->set_value(point.y);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (drag_touching) {
|
||||
if (drag_touching_deaccel) {
|
||||
|
|
@ -584,6 +660,10 @@ PackedStringArray ScrollContainer::get_configuration_warnings() const {
|
|||
return warnings;
|
||||
}
|
||||
|
||||
void ScrollContainer::set_scroll_on_drag_hover(bool p_scroll) {
|
||||
scroll_on_drag_hover = p_scroll;
|
||||
}
|
||||
|
||||
HScrollBar *ScrollContainer::get_h_scroll_bar() {
|
||||
return h_scroll;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SCROLL_CONTAINER_H
|
||||
#define SCROLL_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "container.h"
|
||||
|
||||
|
|
@ -63,12 +62,15 @@ private:
|
|||
bool drag_touching = false;
|
||||
bool drag_touching_deaccel = false;
|
||||
bool beyond_deadzone = false;
|
||||
bool scroll_on_drag_hover = false;
|
||||
|
||||
ScrollMode horizontal_scroll_mode = SCROLL_MODE_AUTO;
|
||||
ScrollMode vertical_scroll_mode = SCROLL_MODE_AUTO;
|
||||
|
||||
int deadzone = 0;
|
||||
bool follow_focus = false;
|
||||
int scroll_border = 20;
|
||||
int scroll_speed = 12;
|
||||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> panel_style;
|
||||
|
|
@ -82,6 +84,7 @@ private:
|
|||
|
||||
bool draw_focus_border = false;
|
||||
bool focus_border_is_drawn = false;
|
||||
bool child_has_focus();
|
||||
|
||||
protected:
|
||||
Size2 get_minimum_size() const override;
|
||||
|
|
@ -96,9 +99,14 @@ protected:
|
|||
void _update_scrollbar_position();
|
||||
void _scroll_moved(float);
|
||||
|
||||
void _accessibility_action_scroll_set(const Variant &p_data);
|
||||
void _accessibility_action_scroll_up(const Variant &p_data);
|
||||
void _accessibility_action_scroll_down(const Variant &p_data);
|
||||
void _accessibility_action_scroll_left(const Variant &p_data);
|
||||
void _accessibility_action_scroll_right(const Variant &p_data);
|
||||
|
||||
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;
|
||||
|
|
@ -124,6 +132,8 @@ public:
|
|||
bool is_following_focus() const;
|
||||
void set_follow_focus(bool p_follow);
|
||||
|
||||
void set_scroll_on_drag_hover(bool p_scroll);
|
||||
|
||||
HScrollBar *get_h_scroll_bar();
|
||||
VScrollBar *get_v_scroll_bar();
|
||||
void ensure_control_visible(Control *p_control);
|
||||
|
|
@ -137,5 +147,3 @@ public:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(ScrollContainer::ScrollMode);
|
||||
|
||||
#endif // SCROLL_CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SEPARATOR_H
|
||||
#define SEPARATOR_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
class Separator : public Control {
|
||||
|
|
@ -66,5 +65,3 @@ class HSeparator : public Separator {
|
|||
public:
|
||||
HSeparator();
|
||||
};
|
||||
|
||||
#endif // SEPARATOR_H
|
||||
|
|
|
|||
|
|
@ -89,12 +89,12 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
} else if (scrollable) {
|
||||
if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
|
||||
if (get_focus_mode() != FOCUS_NONE) {
|
||||
if (get_focus_mode_with_recursive() != FOCUS_NONE) {
|
||||
grab_focus();
|
||||
}
|
||||
set_value(get_value() + get_step());
|
||||
} else if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
|
||||
if (get_focus_mode() != FOCUS_NONE) {
|
||||
if (get_focus_mode_with_recursive() != FOCUS_NONE) {
|
||||
grab_focus();
|
||||
}
|
||||
set_value(get_value() - get_step());
|
||||
|
|
@ -237,7 +237,13 @@ void Slider::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_SLIDER);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_THEME_CHANGED: {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SLIDER_H
|
||||
#define SLIDER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/range.h"
|
||||
|
||||
|
|
@ -111,5 +110,3 @@ public:
|
|||
VSlider() :
|
||||
Slider(VERTICAL) { set_h_size_flags(0); }
|
||||
};
|
||||
|
||||
#endif // SLIDER_H
|
||||
|
|
|
|||
|
|
@ -34,6 +34,50 @@
|
|||
#include "core/math/expression.h"
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
void SpinBoxLineEdit::_accessibility_action_inc(const Variant &p_data) {
|
||||
SpinBox *parent_sb = Object::cast_to<SpinBox>(get_parent());
|
||||
if (parent_sb) {
|
||||
double step = ((parent_sb->get_step() > 0) ? parent_sb->get_step() : 1);
|
||||
parent_sb->set_value(parent_sb->get_value() + step);
|
||||
}
|
||||
}
|
||||
|
||||
void SpinBoxLineEdit::_accessibility_action_dec(const Variant &p_data) {
|
||||
SpinBox *parent_sb = Object::cast_to<SpinBox>(get_parent());
|
||||
if (parent_sb) {
|
||||
double step = ((parent_sb->get_step() > 0) ? parent_sb->get_step() : 1);
|
||||
parent_sb->set_value(parent_sb->get_value() - step);
|
||||
}
|
||||
}
|
||||
|
||||
void SpinBoxLineEdit::_notification(int p_what) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
SpinBox *parent_sb = Object::cast_to<SpinBox>(get_parent());
|
||||
if (parent_sb) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_SPIN_BUTTON);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(ae, parent_sb->get_accessibility_name());
|
||||
DisplayServer::get_singleton()->accessibility_update_set_description(ae, parent_sb->get_accessibility_description());
|
||||
DisplayServer::get_singleton()->accessibility_update_set_live(ae, parent_sb->get_accessibility_live());
|
||||
DisplayServer::get_singleton()->accessibility_update_set_num_value(ae, parent_sb->get_value());
|
||||
DisplayServer::get_singleton()->accessibility_update_set_num_range(ae, parent_sb->get_min(), parent_sb->get_max());
|
||||
if (parent_sb->get_step() > 0) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_num_step(ae, parent_sb->get_step());
|
||||
} else {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_num_step(ae, 1);
|
||||
}
|
||||
//DisplayServer::get_singleton()->accessibility_update_set_num_jump(ae, ???);
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_DECREMENT, callable_mp(this, &SpinBoxLineEdit::_accessibility_action_dec));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_INCREMENT, callable_mp(this, &SpinBoxLineEdit::_accessibility_action_inc));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Size2 SpinBox::get_minimum_size() const {
|
||||
Size2 ms = line_edit->get_combined_minimum_size();
|
||||
ms.width += sizing_cache.buttons_block_width;
|
||||
|
|
@ -63,21 +107,40 @@ void SpinBox::_update_text(bool p_only_update_if_value_changed) {
|
|||
value += " " + suffix;
|
||||
}
|
||||
}
|
||||
|
||||
if (!accepted && update_on_text_changed && !line_edit->get_text().replace_char(',', '.').contains_char('.')) {
|
||||
value = String::num(get_value(), 0);
|
||||
}
|
||||
|
||||
line_edit->set_text_with_selection(value);
|
||||
}
|
||||
|
||||
void SpinBox::_text_submitted(const String &p_string) {
|
||||
if (p_string.is_empty()) {
|
||||
_update_text();
|
||||
return;
|
||||
}
|
||||
|
||||
String text = p_string;
|
||||
|
||||
if (update_on_text_changed) {
|
||||
// Convert commas ',' to dots '.' for French/German etc. keyboard layouts.
|
||||
text = p_string.replace_char(',', '.');
|
||||
|
||||
if (!text.begins_with(".") && p_string.ends_with(".")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (text.begins_with(".")) {
|
||||
line_edit->set_text("0.");
|
||||
line_edit->set_caret_column(line_edit->get_text().length());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Expression> expr;
|
||||
expr.instantiate();
|
||||
|
||||
// Convert commas ',' to dots '.' for French/German etc. keyboard layouts.
|
||||
String text = p_string.replace(",", ".");
|
||||
text = text.replace(";", ",");
|
||||
text = text.replace_char(';', ',');
|
||||
text = TS->parse_number(text);
|
||||
// Ignore the prefix and suffix in the expression.
|
||||
text = text.trim_prefix(prefix + " ").trim_suffix(" " + suffix);
|
||||
|
|
@ -107,12 +170,17 @@ void SpinBox::_text_submitted(const String &p_string) {
|
|||
}
|
||||
|
||||
void SpinBox::_text_changed(const String &p_string) {
|
||||
accepted = false;
|
||||
int cursor_pos = line_edit->get_caret_column();
|
||||
|
||||
_text_submitted(p_string);
|
||||
|
||||
String text = p_string.replace_char(',', '.');
|
||||
|
||||
// Line edit 'set_text' method resets the cursor position so we need to undo that.
|
||||
line_edit->set_caret_column(cursor_pos);
|
||||
if (update_on_text_changed && !text.begins_with(".")) {
|
||||
line_edit->set_caret_column(cursor_pos);
|
||||
}
|
||||
}
|
||||
|
||||
LineEdit *SpinBox::get_line_edit() {
|
||||
|
|
@ -192,6 +260,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
|
|||
if (mb.is_valid() && mb->is_pressed()) {
|
||||
switch (mb->get_button_index()) {
|
||||
case MouseButton::LEFT: {
|
||||
accepted = true;
|
||||
line_edit->grab_focus();
|
||||
|
||||
if (mouse_on_up_button || mouse_on_down_button) {
|
||||
|
|
@ -268,7 +337,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
|
|||
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);
|
||||
double diff_y = -0.01 * Math::pow(Math::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) {
|
||||
|
|
@ -291,9 +360,12 @@ void SpinBox::_line_edit_editing_toggled(bool p_toggled_on) {
|
|||
line_edit->select_all();
|
||||
}
|
||||
} else {
|
||||
accepted = true;
|
||||
|
||||
if (Input::get_singleton()->is_action_pressed("ui_cancel") || line_edit->get_text().is_empty()) {
|
||||
_update_text(); // Revert text if editing was canceled.
|
||||
} else {
|
||||
line_edit->set_text(line_edit->get_text().trim_suffix(".").trim_suffix(","));
|
||||
_update_text(true); // Update text in case value was changed this frame (e.g. on `focus_exited`).
|
||||
_text_submitted(line_edit->get_text());
|
||||
}
|
||||
|
|
@ -622,7 +694,7 @@ void SpinBox::_bind_methods() {
|
|||
}
|
||||
|
||||
SpinBox::SpinBox() {
|
||||
line_edit = memnew(LineEdit);
|
||||
line_edit = memnew(SpinBoxLineEdit);
|
||||
line_edit->set_emoji_menu_enabled(false);
|
||||
add_child(line_edit, false, INTERNAL_MODE_FRONT);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,18 +28,33 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SPIN_BOX_H
|
||||
#define SPIN_BOX_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/line_edit.h"
|
||||
#include "scene/gui/range.h"
|
||||
#include "scene/main/timer.h"
|
||||
|
||||
class SpinBoxLineEdit : public LineEdit {
|
||||
GDCLASS(SpinBoxLineEdit, LineEdit);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
|
||||
static void _bind_methods() {}
|
||||
|
||||
void _accessibility_action_inc(const Variant &p_data);
|
||||
void _accessibility_action_dec(const Variant &p_data);
|
||||
|
||||
public:
|
||||
SpinBoxLineEdit() {}
|
||||
};
|
||||
|
||||
class SpinBox : public Range {
|
||||
GDCLASS(SpinBox, Range);
|
||||
|
||||
LineEdit *line_edit = nullptr;
|
||||
SpinBoxLineEdit *line_edit = nullptr;
|
||||
bool update_on_text_changed = false;
|
||||
bool accepted = true;
|
||||
|
||||
struct SizingCache {
|
||||
int buttons_block_width = 0;
|
||||
|
|
@ -173,5 +188,3 @@ public:
|
|||
|
||||
SpinBox();
|
||||
};
|
||||
|
||||
#endif // SPIN_BOX_H
|
||||
|
|
|
|||
|
|
@ -91,8 +91,59 @@ Control::CursorShape SplitContainerDragger::get_cursor_shape(const Point2 &p_pos
|
|||
return Control::get_cursor_shape(p_pos);
|
||||
}
|
||||
|
||||
void SplitContainerDragger::_accessibility_action_inc(const Variant &p_data) {
|
||||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
|
||||
|
||||
if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || !sc->dragging_enabled) {
|
||||
return;
|
||||
}
|
||||
sc->split_offset -= 10;
|
||||
sc->_compute_split_offset(true);
|
||||
sc->queue_sort();
|
||||
}
|
||||
|
||||
void SplitContainerDragger::_accessibility_action_dec(const Variant &p_data) {
|
||||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
|
||||
|
||||
if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || !sc->dragging_enabled) {
|
||||
return;
|
||||
}
|
||||
sc->split_offset += 10;
|
||||
sc->_compute_split_offset(true);
|
||||
sc->queue_sort();
|
||||
}
|
||||
|
||||
void SplitContainerDragger::_accessibility_action_set_value(const Variant &p_data) {
|
||||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
|
||||
|
||||
if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || !sc->dragging_enabled) {
|
||||
return;
|
||||
}
|
||||
sc->split_offset = p_data;
|
||||
sc->_compute_split_offset(true);
|
||||
sc->queue_sort();
|
||||
}
|
||||
|
||||
void SplitContainerDragger::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_SPLITTER);
|
||||
|
||||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
|
||||
if (sc->collapsed || !sc->_get_sortable_child(0) || !sc->_get_sortable_child(1) || !sc->dragging_enabled) {
|
||||
return;
|
||||
}
|
||||
sc->_compute_split_offset(true);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_num_value(ae, sc->split_offset);
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_DECREMENT, callable_mp(this, &SplitContainerDragger::_accessibility_action_dec));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_INCREMENT, callable_mp(this, &SplitContainerDragger::_accessibility_action_inc));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SET_VALUE, callable_mp(this, &SplitContainerDragger::_accessibility_action_set_value));
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_MOUSE_ENTER: {
|
||||
mouse_inside = true;
|
||||
SplitContainer *sc = Object::cast_to<SplitContainer>(get_parent());
|
||||
|
|
@ -124,6 +175,10 @@ void SplitContainerDragger::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
SplitContainerDragger::SplitContainerDragger() {
|
||||
set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
}
|
||||
|
||||
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++) {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SPLIT_CONTAINER_H
|
||||
#define SPLIT_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -42,6 +41,10 @@ protected:
|
|||
void _notification(int p_what);
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
void _accessibility_action_inc(const Variant &p_data);
|
||||
void _accessibility_action_dec(const Variant &p_data);
|
||||
void _accessibility_action_set_value(const Variant &p_data);
|
||||
|
||||
private:
|
||||
bool dragging = false;
|
||||
int drag_from = 0;
|
||||
|
|
@ -50,6 +53,8 @@ private:
|
|||
|
||||
public:
|
||||
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
|
||||
|
||||
SplitContainerDragger();
|
||||
};
|
||||
|
||||
class SplitContainer : public Container {
|
||||
|
|
@ -157,5 +162,3 @@ public:
|
|||
VSplitContainer() :
|
||||
SplitContainer(true) { is_fixed = true; }
|
||||
};
|
||||
|
||||
#endif // SPLIT_CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef SUBVIEWPORT_CONTAINER_H
|
||||
#define SUBVIEWPORT_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
|
||||
|
|
@ -77,5 +76,3 @@ public:
|
|||
|
||||
SubViewportContainer();
|
||||
};
|
||||
|
||||
#endif // SUBVIEWPORT_CONTAINER_H
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@ Size2 TabBar::get_minimum_size() const {
|
|||
if (ms.width - ofs > style->get_minimum_size().width) {
|
||||
ms.width -= theme_cache.h_separation;
|
||||
}
|
||||
|
||||
if (i < tabs.size() - 1) {
|
||||
ms.width += theme_cache.tab_separation;
|
||||
}
|
||||
}
|
||||
|
||||
if (clip_tabs) {
|
||||
|
|
@ -203,6 +207,10 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
|
|||
queue_redraw();
|
||||
}
|
||||
|
||||
if (close_with_middle_mouse && mb->is_pressed() && mb->get_button_index() == MouseButton::MIDDLE) {
|
||||
emit_signal(SNAME("tab_close_pressed"), hover);
|
||||
}
|
||||
|
||||
if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || (select_with_rmb && mb->get_button_index() == MouseButton::RIGHT))) {
|
||||
Point2 pos = mb->get_position();
|
||||
|
||||
|
|
@ -347,6 +355,35 @@ void TabBar::_shape(int p_tab) {
|
|||
tabs.write[p_tab].text_buf->add_string(atr(tabs[p_tab].text), theme_cache.font, theme_cache.font_size, tabs[p_tab].language);
|
||||
}
|
||||
|
||||
RID TabBar::get_tab_accessibility_element(int p_tab) const {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND_V(ae.is_null(), RID());
|
||||
|
||||
const Tab &item = tabs[p_tab];
|
||||
if (item.accessibility_item_element.is_null()) {
|
||||
item.accessibility_item_element = DisplayServer::get_singleton()->accessibility_create_sub_element(ae, DisplayServer::AccessibilityRole::ROLE_TAB);
|
||||
item.accessibility_item_dirty = true;
|
||||
}
|
||||
return item.accessibility_item_element;
|
||||
}
|
||||
|
||||
RID TabBar::get_focused_accessibility_element() const {
|
||||
if (current == -1) {
|
||||
return get_accessibility_element();
|
||||
} else {
|
||||
const Tab &item = tabs[current];
|
||||
return item.accessibility_item_element;
|
||||
}
|
||||
}
|
||||
|
||||
void TabBar::_accessibility_action_scroll_into_view(const Variant &p_data, int p_index) {
|
||||
ensure_tab_visible(p_index);
|
||||
}
|
||||
|
||||
void TabBar::_accessibility_action_focus(const Variant &p_data, int p_index) {
|
||||
set_current_tab(p_index);
|
||||
}
|
||||
|
||||
void TabBar::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
|
|
@ -379,6 +416,46 @@ void TabBar::_notification(int p_what) {
|
|||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
|
||||
for (int i = 0; i < tabs.size(); i++) {
|
||||
tabs.write[i].accessibility_item_element = RID();
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_TAB_BAR);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_count(ae, tabs.size());
|
||||
|
||||
for (int i = 0; i < tabs.size(); i++) {
|
||||
const Tab &item = tabs[i];
|
||||
|
||||
if (item.accessibility_item_element.is_null()) {
|
||||
item.accessibility_item_element = DisplayServer::get_singleton()->accessibility_create_sub_element(ae, DisplayServer::AccessibilityRole::ROLE_TAB);
|
||||
item.accessibility_item_dirty = true;
|
||||
}
|
||||
|
||||
if (item.accessibility_item_dirty) {
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(item.accessibility_item_element, DisplayServer::AccessibilityAction::ACTION_SCROLL_INTO_VIEW, callable_mp(this, &TabBar::_accessibility_action_scroll_into_view).bind(i));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(item.accessibility_item_element, DisplayServer::AccessibilityAction::ACTION_FOCUS, callable_mp(this, &TabBar::_accessibility_action_focus).bind(i));
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_index(item.accessibility_item_element, i);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(item.accessibility_item_element, atr(item.text));
|
||||
DisplayServer::get_singleton()->accessibility_update_set_list_item_selected(item.accessibility_item_element, i == current);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(item.accessibility_item_element, DisplayServer::AccessibilityFlags::FLAG_DISABLED, item.disabled);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(item.accessibility_item_element, DisplayServer::AccessibilityFlags::FLAG_HIDDEN, item.hidden);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_tooltip(item.accessibility_item_element, item.tooltip);
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_bounds(item.accessibility_item_element, Rect2(Point2(item.ofs_cache, 0), Size2(item.size_cache, get_size().height)));
|
||||
|
||||
item.accessibility_item_dirty = false;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
|
||||
queue_redraw();
|
||||
} break;
|
||||
|
|
@ -389,6 +466,7 @@ void TabBar::_notification(int p_what) {
|
|||
_shape(i);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
|
||||
|
|
@ -430,9 +508,12 @@ void TabBar::_notification(int p_what) {
|
|||
int limit_minus_buttons = size.width - theme_cache.increment_icon->get_width() - theme_cache.decrement_icon->get_width();
|
||||
|
||||
int ofs = tabs[offset].ofs_cache;
|
||||
int tab_separation_offset = 0;
|
||||
|
||||
// Draw unselected tabs in the back.
|
||||
for (int i = offset; i <= max_drawn_tab; i++) {
|
||||
tab_separation_offset = (i - offset) * theme_cache.tab_separation;
|
||||
|
||||
if (tabs[i].hidden) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -452,7 +533,7 @@ void TabBar::_notification(int p_what) {
|
|||
col = theme_cache.font_unselected_color;
|
||||
}
|
||||
|
||||
_draw_tab(sb, col, i, rtl ? size.width - ofs - tabs[i].size_cache : ofs, false);
|
||||
_draw_tab(sb, col, i, rtl ? (size.width - ofs - tab_separation_offset - tabs[i].size_cache) : (ofs + tab_separation_offset), false);
|
||||
}
|
||||
|
||||
ofs += tabs[i].size_cache;
|
||||
|
|
@ -460,8 +541,13 @@ void TabBar::_notification(int p_what) {
|
|||
|
||||
// Draw selected tab in the front, but only if it's visible.
|
||||
if (current >= offset && current <= max_drawn_tab && !tabs[current].hidden) {
|
||||
tab_separation_offset = (current - offset) * theme_cache.tab_separation;
|
||||
if (tab_alignment == ALIGNMENT_LEFT && (current - offset) > 1) {
|
||||
tab_separation_offset = theme_cache.tab_separation;
|
||||
}
|
||||
|
||||
Ref<StyleBox> sb = tabs[current].disabled ? theme_cache.tab_disabled_style : theme_cache.tab_selected_style;
|
||||
float x = rtl ? size.width - tabs[current].ofs_cache - tabs[current].size_cache : tabs[current].ofs_cache;
|
||||
float x = rtl ? (size.width - tabs[current].ofs_cache - tab_separation_offset - tabs[current].size_cache) : (tabs[current].ofs_cache + tab_separation_offset);
|
||||
|
||||
_draw_tab(sb, theme_cache.font_selected_color, current, x, has_focus());
|
||||
}
|
||||
|
|
@ -499,13 +585,41 @@ void TabBar::_notification(int p_what) {
|
|||
if (dragging_valid_tab) {
|
||||
int x;
|
||||
|
||||
int tab_hover = get_hovered_tab();
|
||||
if (tab_hover != -1) {
|
||||
Rect2 tab_rect = get_tab_rect(tab_hover);
|
||||
int closest_tab = get_closest_tab_idx_to_point(get_local_mouse_position());
|
||||
if (closest_tab != -1) {
|
||||
Rect2 tab_rect = get_tab_rect(closest_tab);
|
||||
|
||||
x = tab_rect.position.x;
|
||||
if (get_local_mouse_position().x > x + tab_rect.size.width / 2) {
|
||||
x += tab_rect.size.width;
|
||||
// Calculate midpoint between tabs.
|
||||
if (rtl) {
|
||||
if (get_local_mouse_position().x > tab_rect.position.x + tab_rect.size.width / 2) {
|
||||
if (closest_tab > 0) { // On right side of closest_tab and not first tab.
|
||||
Rect2 next_tab_rect = get_tab_rect(closest_tab - 1);
|
||||
x = (tab_rect.position.x + tab_rect.size.width + next_tab_rect.position.x) / 2;
|
||||
} else { // First tab, will appear on right edge.
|
||||
x = tab_rect.position.x + tab_rect.size.width;
|
||||
}
|
||||
} else {
|
||||
if (closest_tab < max_drawn_tab) { // On left side of closest_tab and not last tab.
|
||||
Rect2 prev_tab_rect = get_tab_rect(closest_tab + 1);
|
||||
x = (tab_rect.position.x + prev_tab_rect.position.x + prev_tab_rect.size.width) / 2;
|
||||
} else { // Last tab, will appear on left edge.
|
||||
x = tab_rect.position.x;
|
||||
}
|
||||
}
|
||||
} else if (get_local_mouse_position().x > tab_rect.position.x + tab_rect.size.width / 2) {
|
||||
if (closest_tab < max_drawn_tab) { // On right side of closest_tab and not last tab.
|
||||
Rect2 next_tab_rect = get_tab_rect(closest_tab + 1);
|
||||
x = (tab_rect.position.x + tab_rect.size.width + next_tab_rect.position.x) / 2;
|
||||
} else { // Last tab, will appear on right edge.
|
||||
x = tab_rect.position.x + tab_rect.size.width;
|
||||
}
|
||||
} else {
|
||||
if (closest_tab > 0) { // On left side of closest_tab and not first tab.
|
||||
Rect2 prev_tab_rect = get_tab_rect(closest_tab - 1);
|
||||
x = (tab_rect.position.x + prev_tab_rect.position.x + prev_tab_rect.size.width) / 2;
|
||||
} else { // First tab, will appear on left edge.
|
||||
x = tab_rect.position.x;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rtl ^ (get_local_mouse_position().x < get_tab_rect(0).position.x)) {
|
||||
|
|
@ -632,6 +746,15 @@ void TabBar::set_tab_count(int p_count) {
|
|||
}
|
||||
|
||||
ERR_FAIL_COND(p_count < 0);
|
||||
|
||||
if (tabs.size() > p_count) {
|
||||
for (int i = p_count; i < tabs.size(); i++) {
|
||||
if (tabs[i].accessibility_item_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(tabs.write[i].accessibility_item_element);
|
||||
tabs.write[i].accessibility_item_element = RID();
|
||||
}
|
||||
}
|
||||
}
|
||||
tabs.resize(p_count);
|
||||
|
||||
if (p_count == 0) {
|
||||
|
|
@ -662,6 +785,7 @@ void TabBar::set_tab_count(int p_count) {
|
|||
}
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
notify_property_list_changed();
|
||||
|
|
@ -697,6 +821,7 @@ void TabBar::set_current_tab(int p_current) {
|
|||
if (scroll_to_selected) {
|
||||
ensure_tab_visible(current);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
|
||||
emit_signal(SNAME("tab_changed"), p_current);
|
||||
|
|
@ -745,6 +870,7 @@ void TabBar::set_tab_offset(int p_offset) {
|
|||
ERR_FAIL_INDEX(p_offset, tabs.size());
|
||||
offset = p_offset;
|
||||
_update_cache();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
|
@ -771,6 +897,7 @@ void TabBar::set_tab_title(int p_tab, const String &p_title) {
|
|||
if (scroll_to_selected) {
|
||||
ensure_tab_visible(current);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
}
|
||||
|
|
@ -783,6 +910,7 @@ String TabBar::get_tab_title(int p_tab) const {
|
|||
void TabBar::set_tab_tooltip(int p_tab, const String &p_tooltip) {
|
||||
ERR_FAIL_INDEX(p_tab, tabs.size());
|
||||
tabs.write[p_tab].tooltip = p_tooltip;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
String TabBar::get_tab_tooltip(int p_tab) const {
|
||||
|
|
@ -796,7 +924,9 @@ void TabBar::set_tab_text_direction(int p_tab, Control::TextDirection p_text_dir
|
|||
|
||||
if (tabs[p_tab].text_direction != p_text_direction) {
|
||||
tabs.write[p_tab].text_direction = p_text_direction;
|
||||
|
||||
_shape(p_tab);
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
|
@ -811,12 +941,14 @@ void TabBar::set_tab_language(int p_tab, const String &p_language) {
|
|||
|
||||
if (tabs[p_tab].language != p_language) {
|
||||
tabs.write[p_tab].language = p_language;
|
||||
|
||||
_shape(p_tab);
|
||||
_update_cache();
|
||||
_ensure_no_over_offset();
|
||||
if (scroll_to_selected) {
|
||||
ensure_tab_visible(current);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
}
|
||||
|
|
@ -887,6 +1019,7 @@ void TabBar::set_tab_disabled(int p_tab, bool p_disabled) {
|
|||
if (scroll_to_selected) {
|
||||
ensure_tab_visible(current);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
}
|
||||
|
|
@ -910,6 +1043,7 @@ void TabBar::set_tab_hidden(int p_tab, bool p_hidden) {
|
|||
if (scroll_to_selected) {
|
||||
ensure_tab_visible(current);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
}
|
||||
|
|
@ -1053,12 +1187,17 @@ void TabBar::_update_cache(bool p_update_hover) {
|
|||
}
|
||||
|
||||
w += tabs[i].size_cache;
|
||||
if ((i - offset) > 0) {
|
||||
w += theme_cache.tab_separation;
|
||||
}
|
||||
|
||||
// Check if all tabs would fit inside the area.
|
||||
if (clip_tabs && i > offset && (w > limit || (offset > 0 && w > limit_minus_buttons))) {
|
||||
tabs.write[i].ofs_cache = 0;
|
||||
|
||||
w -= tabs[i].size_cache;
|
||||
w -= theme_cache.tab_separation;
|
||||
|
||||
max_drawn_tab = i - 1;
|
||||
|
||||
while (w > limit_minus_buttons && max_drawn_tab > offset) {
|
||||
|
|
@ -1066,11 +1205,14 @@ void TabBar::_update_cache(bool p_update_hover) {
|
|||
|
||||
if (!tabs[max_drawn_tab].hidden) {
|
||||
w -= tabs[max_drawn_tab].size_cache;
|
||||
w -= theme_cache.tab_separation;
|
||||
}
|
||||
|
||||
max_drawn_tab--;
|
||||
}
|
||||
}
|
||||
|
||||
tabs.write[i].accessibility_item_dirty = true;
|
||||
}
|
||||
|
||||
missing_right = max_drawn_tab < tabs.size() - 1;
|
||||
|
|
@ -1125,6 +1267,7 @@ void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) {
|
|||
if (scroll_to_selected) {
|
||||
ensure_tab_visible(current);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
|
||||
|
|
@ -1143,12 +1286,19 @@ void TabBar::clear_tabs() {
|
|||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < tabs.size(); i++) {
|
||||
if (tabs[i].accessibility_item_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(tabs.write[i].accessibility_item_element);
|
||||
tabs.write[i].accessibility_item_element = RID();
|
||||
}
|
||||
}
|
||||
tabs.clear();
|
||||
offset = 0;
|
||||
max_drawn_tab = 0;
|
||||
current = -1;
|
||||
previous = -1;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
notify_property_list_changed();
|
||||
|
|
@ -1156,6 +1306,11 @@ void TabBar::clear_tabs() {
|
|||
|
||||
void TabBar::remove_tab(int p_idx) {
|
||||
ERR_FAIL_INDEX(p_idx, tabs.size());
|
||||
|
||||
if (tabs[p_idx].accessibility_item_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(tabs.write[p_idx].accessibility_item_element);
|
||||
tabs.write[p_idx].accessibility_item_element = RID();
|
||||
}
|
||||
tabs.remove_at(p_idx);
|
||||
|
||||
bool is_tab_changing = current == p_idx;
|
||||
|
|
@ -1205,6 +1360,7 @@ void TabBar::remove_tab(int p_idx) {
|
|||
}
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
notify_property_list_changed();
|
||||
|
|
@ -1238,7 +1394,7 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
|
|||
}
|
||||
|
||||
Variant TabBar::_handle_get_drag_data(const String &p_type, const Point2 &p_point) {
|
||||
int tab_over = get_tab_idx_at_point(p_point);
|
||||
int tab_over = (p_point == Vector2(Math::INF, Math::INF)) ? current : get_tab_idx_at_point(p_point);
|
||||
if (tab_over < 0) {
|
||||
return Variant();
|
||||
}
|
||||
|
|
@ -1303,7 +1459,7 @@ void TabBar::_handle_drop_data(const String &p_type, const Point2 &p_point, cons
|
|||
|
||||
if (String(d["type"]) == p_type) {
|
||||
int tab_from_id = d["tab_index"];
|
||||
int hover_now = get_tab_idx_at_point(p_point);
|
||||
int hover_now = (p_point == Vector2(Math::INF, Math::INF)) ? current : get_closest_tab_idx_to_point(p_point);
|
||||
NodePath from_path = d["from_path"];
|
||||
NodePath to_path = get_path();
|
||||
|
||||
|
|
@ -1361,6 +1517,8 @@ void TabBar::_handle_drop_data(const String &p_type, const Point2 &p_point, cons
|
|||
|
||||
void TabBar::_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index) {
|
||||
Tab moving_tab = p_from_tabbar->tabs[p_from_index];
|
||||
moving_tab.accessibility_item_element = RID();
|
||||
moving_tab.accessibility_item_dirty = true;
|
||||
p_from_tabbar->remove_tab(p_from_index);
|
||||
tabs.insert(p_to_index, moving_tab);
|
||||
|
||||
|
|
@ -1380,6 +1538,7 @@ void TabBar::_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_in
|
|||
queue_redraw();
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
update_minimum_size();
|
||||
}
|
||||
|
||||
|
|
@ -1398,6 +1557,24 @@ int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {
|
|||
return hover_now;
|
||||
}
|
||||
|
||||
int TabBar::get_closest_tab_idx_to_point(const Point2 &p_point) const {
|
||||
int closest_tab = get_tab_idx_at_point(p_point); // See if we're hovering over a tab first.
|
||||
|
||||
if (closest_tab == -1) { // Didn't find a tab, so get the closest one.
|
||||
float closest_distance = FLT_MAX;
|
||||
for (int i = offset; i <= max_drawn_tab; i++) {
|
||||
Vector2 center = get_tab_rect(i).get_center();
|
||||
float distance = center.distance_to(p_point);
|
||||
if (distance < closest_distance) {
|
||||
closest_distance = distance;
|
||||
closest_tab = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest_tab;
|
||||
}
|
||||
|
||||
void TabBar::set_tab_alignment(AlignmentMode p_alignment) {
|
||||
ERR_FAIL_INDEX(p_alignment, ALIGNMENT_MAX);
|
||||
|
||||
|
|
@ -1451,6 +1628,8 @@ void TabBar::move_tab(int p_from, int p_to) {
|
|||
ERR_FAIL_INDEX(p_to, tabs.size());
|
||||
|
||||
Tab tab_from = tabs[p_from];
|
||||
tab_from.accessibility_item_dirty = true;
|
||||
|
||||
tabs.remove_at(p_from);
|
||||
tabs.insert(p_to, tab_from);
|
||||
|
||||
|
|
@ -1475,6 +1654,7 @@ void TabBar::move_tab(int p_from, int p_to) {
|
|||
if (scroll_to_selected) {
|
||||
ensure_tab_visible(current);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
notify_property_list_changed();
|
||||
}
|
||||
|
|
@ -1647,11 +1827,23 @@ void TabBar::ensure_tab_visible(int p_idx) {
|
|||
|
||||
Rect2 TabBar::get_tab_rect(int p_tab) const {
|
||||
ERR_FAIL_INDEX_V(p_tab, tabs.size(), Rect2());
|
||||
if (is_layout_rtl()) {
|
||||
return Rect2(get_size().width - tabs[p_tab].ofs_cache - tabs[p_tab].size_cache, 0, tabs[p_tab].size_cache, get_size().height);
|
||||
} else {
|
||||
return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height);
|
||||
int tab_separation_offset = (p_tab - offset) * theme_cache.tab_separation;
|
||||
if (tab_alignment == ALIGNMENT_LEFT && (p_tab - offset) > 1) {
|
||||
tab_separation_offset = theme_cache.tab_separation;
|
||||
}
|
||||
if (is_layout_rtl()) {
|
||||
return Rect2(get_size().width - tabs[p_tab].ofs_cache - tab_separation_offset - tabs[p_tab].size_cache, 0, tabs[p_tab].size_cache, get_size().height);
|
||||
} else {
|
||||
return Rect2(tabs[p_tab].ofs_cache + tab_separation_offset, 0, tabs[p_tab].size_cache, get_size().height);
|
||||
}
|
||||
}
|
||||
|
||||
void TabBar::set_close_with_middle_mouse(bool p_scroll_close) {
|
||||
close_with_middle_mouse = p_scroll_close;
|
||||
}
|
||||
|
||||
bool TabBar::get_close_with_middle_mouse() const {
|
||||
return close_with_middle_mouse;
|
||||
}
|
||||
|
||||
void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) {
|
||||
|
|
@ -1795,6 +1987,8 @@ void TabBar::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("ensure_tab_visible", "idx"), &TabBar::ensure_tab_visible);
|
||||
ClassDB::bind_method(D_METHOD("get_tab_rect", "tab_idx"), &TabBar::get_tab_rect);
|
||||
ClassDB::bind_method(D_METHOD("move_tab", "from", "to"), &TabBar::move_tab);
|
||||
ClassDB::bind_method(D_METHOD("set_close_with_middle_mouse", "enabled"), &TabBar::set_close_with_middle_mouse);
|
||||
ClassDB::bind_method(D_METHOD("get_close_with_middle_mouse"), &TabBar::get_close_with_middle_mouse);
|
||||
ClassDB::bind_method(D_METHOD("set_tab_close_display_policy", "policy"), &TabBar::set_tab_close_display_policy);
|
||||
ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &TabBar::get_tab_close_display_policy);
|
||||
ClassDB::bind_method(D_METHOD("set_max_tab_width", "width"), &TabBar::set_max_tab_width);
|
||||
|
|
@ -1825,6 +2019,7 @@ void TabBar::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1"), "set_current_tab", "get_current_tab");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "close_with_middle_mouse"), "set_close_with_middle_mouse", "get_close_with_middle_mouse");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_tab_width", PROPERTY_HINT_RANGE, "0,99999,1,suffix:px"), "set_max_tab_width", "get_max_tab_width");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
|
||||
|
|
@ -1847,6 +2042,7 @@ void TabBar::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(CLOSE_BUTTON_MAX);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabBar, h_separation);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabBar, tab_separation);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabBar, icon_max_width);
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabBar, tab_unselected_style, "tab_unselected");
|
||||
|
|
@ -1888,6 +2084,7 @@ void TabBar::_bind_methods() {
|
|||
}
|
||||
|
||||
TabBar::TabBar() {
|
||||
set_focus_mode(FOCUS_ACCESSIBILITY);
|
||||
set_size(Size2(get_size().width, get_minimum_size().height));
|
||||
set_focus_mode(FOCUS_ALL);
|
||||
connect(SceneStringName(mouse_exited), callable_mp(this, &TabBar::_on_mouse_exited));
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TAB_BAR_H
|
||||
#define TAB_BAR_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/property_list_helper.h"
|
||||
|
|
@ -55,6 +54,9 @@ public:
|
|||
|
||||
private:
|
||||
struct Tab {
|
||||
mutable RID accessibility_item_element;
|
||||
mutable bool accessibility_item_dirty = true;
|
||||
|
||||
String text;
|
||||
String tooltip;
|
||||
|
||||
|
|
@ -108,6 +110,7 @@ private:
|
|||
int cb_hover = -1;
|
||||
bool cb_pressing = false;
|
||||
CloseButtonDisplayPolicy cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER;
|
||||
bool close_with_middle_mouse = true;
|
||||
|
||||
int hover = -1; // Hovered tab.
|
||||
int max_width = 0;
|
||||
|
|
@ -127,6 +130,7 @@ private:
|
|||
|
||||
struct ThemeCache {
|
||||
int h_separation = 0;
|
||||
int tab_separation = 0;
|
||||
int icon_max_width = 0;
|
||||
|
||||
Ref<StyleBox> tab_unselected_style;
|
||||
|
|
@ -170,6 +174,9 @@ private:
|
|||
void _shape(int p_tab);
|
||||
void _draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x, bool p_focus);
|
||||
|
||||
void _accessibility_action_scroll_into_view(const Variant &p_data, int p_index);
|
||||
void _accessibility_action_focus(const Variant &p_data, int p_index);
|
||||
|
||||
protected:
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
virtual String get_tooltip(const Point2 &p_pos) const override;
|
||||
|
|
@ -188,6 +195,9 @@ protected:
|
|||
void _move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index);
|
||||
|
||||
public:
|
||||
RID get_tab_accessibility_element(int p_tab) const;
|
||||
virtual RID get_focused_accessibility_element() const override;
|
||||
|
||||
Variant _handle_get_drag_data(const String &p_type, const Point2 &p_point);
|
||||
bool _handle_can_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data) const;
|
||||
void _handle_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data, const Callable &p_move_tab_callback, const Callable &p_move_tab_from_other_callback);
|
||||
|
|
@ -225,6 +235,7 @@ public:
|
|||
Ref<Texture2D> get_tab_button_icon(int p_tab) const;
|
||||
|
||||
int get_tab_idx_at_point(const Point2 &p_point) const;
|
||||
int get_closest_tab_idx_to_point(const Point2 &p_point) const;
|
||||
|
||||
void set_tab_alignment(AlignmentMode p_alignment);
|
||||
AlignmentMode get_tab_alignment() const;
|
||||
|
|
@ -239,6 +250,9 @@ public:
|
|||
void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy);
|
||||
CloseButtonDisplayPolicy get_tab_close_display_policy() const;
|
||||
|
||||
void set_close_with_middle_mouse(bool p_scroll_close);
|
||||
bool get_close_with_middle_mouse() const;
|
||||
|
||||
void set_tab_count(int p_count);
|
||||
int get_tab_count() const;
|
||||
|
||||
|
|
@ -288,5 +302,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(TabBar::AlignmentMode);
|
||||
VARIANT_ENUM_CAST(TabBar::CloseButtonDisplayPolicy);
|
||||
|
||||
#endif // TAB_BAR_H
|
||||
|
|
|
|||
|
|
@ -142,6 +142,45 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
void TabContainer::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
|
||||
tab_panels.clear();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
int tab_index = 0;
|
||||
int tab_cur = tab_bar->get_current_tab();
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
Node *child_node = get_child(i);
|
||||
Window *child_wnd = Object::cast_to<Window>(child_node);
|
||||
if (child_wnd && !child_wnd->is_embedded()) {
|
||||
continue;
|
||||
}
|
||||
if (child_node->is_part_of_edited_scene()) {
|
||||
continue;
|
||||
}
|
||||
Control *control = as_sortable_control(child_node, SortableVisibilityMode::IGNORE);
|
||||
if (!control || control == tab_bar || children_removing.has(control)) {
|
||||
DisplayServer::get_singleton()->accessibility_update_add_child(ae, child_node->get_accessibility_element());
|
||||
} else {
|
||||
if (!tab_panels.has(child_node)) {
|
||||
tab_panels[child_node] = DisplayServer::get_singleton()->accessibility_create_sub_element(ae, DisplayServer::AccessibilityRole::ROLE_TAB_PANEL);
|
||||
}
|
||||
RID panel = tab_panels[child_node];
|
||||
RID tab = tab_bar->get_tab_accessibility_element(tab_index);
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_add_related_controls(tab, panel);
|
||||
DisplayServer::get_singleton()->accessibility_update_add_related_labeled_by(panel, tab);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(panel, DisplayServer::AccessibilityFlags::FLAG_HIDDEN, tab_index != tab_cur);
|
||||
DisplayServer::get_singleton()->accessibility_update_add_child(panel, child_node->get_accessibility_element());
|
||||
|
||||
tab_index++;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
// If some nodes happen to be renamed outside the tree, the tab names need to be updated manually.
|
||||
if (get_tab_count() > 0) {
|
||||
|
|
@ -247,6 +286,7 @@ void TabContainer::_on_theme_changed() {
|
|||
tab_bar->add_theme_font_size_override(SceneStringName(font_size), theme_cache.tab_font_size);
|
||||
|
||||
tab_bar->add_theme_constant_override(SNAME("h_separation"), theme_cache.icon_separation);
|
||||
tab_bar->add_theme_constant_override(SNAME("tab_separation"), theme_cache.tab_separation);
|
||||
tab_bar->add_theme_constant_override(SNAME("icon_max_width"), theme_cache.icon_max_width);
|
||||
tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size);
|
||||
|
||||
|
|
@ -555,6 +595,7 @@ void TabContainer::add_child_notify(Node *p_child) {
|
|||
if (get_tab_count() == 1) {
|
||||
queue_redraw();
|
||||
}
|
||||
queue_accessibility_update();
|
||||
|
||||
p_child->connect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names));
|
||||
p_child->connect(SceneStringName(visibility_changed), callable_mp(this, &TabContainer::_on_tab_visibility_changed).bind(c));
|
||||
|
|
@ -578,11 +619,17 @@ void TabContainer::move_child_notify(Node *p_child) {
|
|||
}
|
||||
|
||||
_refresh_tab_indices();
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void TabContainer::remove_child_notify(Node *p_child) {
|
||||
Container::remove_child_notify(p_child);
|
||||
|
||||
if (tab_panels.has(p_child)) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(tab_panels[p_child]);
|
||||
tab_panels.erase(p_child);
|
||||
}
|
||||
|
||||
if (p_child == tab_bar) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -606,6 +653,7 @@ void TabContainer::remove_child_notify(Node *p_child) {
|
|||
if (get_tab_count() == 0) {
|
||||
queue_redraw();
|
||||
}
|
||||
queue_accessibility_update();
|
||||
|
||||
p_child->remove_meta("_tab_index");
|
||||
p_child->remove_meta("_tab_name");
|
||||
|
|
@ -725,7 +773,7 @@ void TabContainer::set_tab_focus_mode(Control::FocusMode p_focus_mode) {
|
|||
}
|
||||
|
||||
Control::FocusMode TabContainer::get_tab_focus_mode() const {
|
||||
return tab_bar->get_focus_mode();
|
||||
return tab_bar->get_focus_mode_with_recursive();
|
||||
}
|
||||
|
||||
void TabContainer::set_clip_tabs(bool p_clip_tabs) {
|
||||
|
|
@ -761,7 +809,6 @@ 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 {
|
||||
|
|
@ -951,7 +998,7 @@ void TabContainer::set_popup(Node *p_popup) {
|
|||
|
||||
Popup *TabContainer::get_popup() const {
|
||||
if (popup_obj_id.is_valid()) {
|
||||
Popup *popup = Object::cast_to<Popup>(ObjectDB::get_instance(popup_obj_id));
|
||||
Popup *popup = ObjectDB::get_instance<Popup>(popup_obj_id);
|
||||
if (popup) {
|
||||
return popup;
|
||||
} else {
|
||||
|
|
@ -1078,6 +1125,7 @@ void TabContainer::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(POSITION_MAX);
|
||||
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, side_margin);
|
||||
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, tab_separation);
|
||||
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, panel_style, "panel");
|
||||
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, tabbar_style, "tabbar_background");
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TAB_CONTAINER_H
|
||||
#define TAB_CONTAINER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/container.h"
|
||||
#include "scene/gui/popup.h"
|
||||
|
|
@ -71,6 +70,7 @@ private:
|
|||
|
||||
// TabBar overrides.
|
||||
int icon_separation = 0;
|
||||
int tab_separation = 0;
|
||||
int icon_max_width = 0;
|
||||
int outline_size = 0;
|
||||
|
||||
|
|
@ -97,6 +97,8 @@ private:
|
|||
int tab_font_size;
|
||||
} theme_cache;
|
||||
|
||||
HashMap<Node *, RID> tab_panels;
|
||||
|
||||
int _get_tab_height() const;
|
||||
Vector<Control *> _get_tab_controls() const;
|
||||
void _on_theme_changed();
|
||||
|
|
@ -129,6 +131,8 @@ protected:
|
|||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual bool accessibility_override_tree_hierarchy() const override { return true; }
|
||||
|
||||
TabBar *get_tab_bar() const;
|
||||
|
||||
int get_tab_idx_at_point(const Point2 &p_point) const;
|
||||
|
|
@ -211,5 +215,3 @@ public:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(TabContainer::TabPosition);
|
||||
|
||||
#endif // TAB_CONTAINER_H
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TEXT_EDIT_H
|
||||
#define TEXT_EDIT_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
|
@ -145,12 +144,15 @@ private:
|
|||
Color color = Color(1, 1, 1);
|
||||
};
|
||||
|
||||
mutable int64_t next_item_id = 0;
|
||||
|
||||
struct Line {
|
||||
Vector<Gutter> gutters;
|
||||
|
||||
String data;
|
||||
Array bidi_override;
|
||||
Ref<TextParagraph> data_buf;
|
||||
Vector<RID> accessibility_text_root_element;
|
||||
|
||||
String ime_data;
|
||||
Array ime_bidi_override;
|
||||
|
|
@ -160,6 +162,7 @@ private:
|
|||
int line_count = 0;
|
||||
int height = 0;
|
||||
int width = 0;
|
||||
float indent_ofs = -1.0;
|
||||
|
||||
Line() {
|
||||
data_buf.instantiate();
|
||||
|
|
@ -227,8 +230,17 @@ private:
|
|||
BitField<TextServer::LineBreakFlag> get_brk_flags() const;
|
||||
int get_line_wrap_amount(int p_line) const;
|
||||
|
||||
const Vector<RID> get_accessibility_elements(int p_line);
|
||||
void update_accessibility(int p_line, RID p_root);
|
||||
void clear_accessibility() {
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
text.write[i].accessibility_text_root_element.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Vector<Vector2i> get_line_wrap_ranges(int p_line) const;
|
||||
const Ref<TextParagraph> get_line_data(int p_line) const;
|
||||
float get_indent_offset(int p_line, bool p_rtl) const;
|
||||
|
||||
void set(int p_line, const String &p_text, const Array &p_bidi_override);
|
||||
void set_ime(int p_line, const String &p_text, const Array &p_bidi_override);
|
||||
|
|
@ -274,13 +286,22 @@ private:
|
|||
|
||||
/* Text */
|
||||
Text text;
|
||||
|
||||
bool setting_text = false;
|
||||
|
||||
enum AltInputMode {
|
||||
ALT_INPUT_NONE,
|
||||
ALT_INPUT_UNICODE,
|
||||
ALT_INPUT_OEM,
|
||||
ALT_INPUT_WIN,
|
||||
};
|
||||
|
||||
AltInputMode alt_mode = ALT_INPUT_NONE;
|
||||
bool alt_start = false;
|
||||
bool alt_start_no_hold = false;
|
||||
uint32_t alt_code = 0;
|
||||
|
||||
bool tab_input_mode = true;
|
||||
|
||||
// Text properties.
|
||||
String ime_text = "";
|
||||
Point2 ime_selection;
|
||||
|
|
@ -539,7 +560,7 @@ private:
|
|||
void _scroll_lines_up();
|
||||
void _scroll_lines_down();
|
||||
|
||||
void _adjust_viewport_to_caret_horizontally(int p_caret = 0);
|
||||
void _adjust_viewport_to_caret_horizontally(int p_caret = 0, bool p_maximize_selection = true);
|
||||
|
||||
// Minimap.
|
||||
bool draw_minimap = false;
|
||||
|
|
@ -627,6 +648,8 @@ private:
|
|||
bool draw_tabs = false;
|
||||
bool draw_spaces = false;
|
||||
|
||||
RID accessibility_text_root_element_nl;
|
||||
|
||||
/*** Super internal Core API. Everything builds on it. ***/
|
||||
bool text_changed_dirty = false;
|
||||
void _text_changed();
|
||||
|
|
@ -695,6 +718,8 @@ protected:
|
|||
void _unhide_all_lines();
|
||||
virtual void _unhide_carets();
|
||||
|
||||
int _get_wrapped_indent_level(int p_line, int &r_first_wrap) const;
|
||||
|
||||
// Symbol lookup.
|
||||
String lookup_symbol_word;
|
||||
void _set_symbol_lookup_word(const String &p_symbol);
|
||||
|
|
@ -715,6 +740,17 @@ protected:
|
|||
virtual void _paste_internal(int p_caret);
|
||||
virtual void _paste_primary_clipboard_internal(int p_caret);
|
||||
|
||||
void _accessibility_action_set_selection(const Variant &p_data);
|
||||
void _accessibility_action_replace_selected(const Variant &p_data);
|
||||
void _accessibility_action_set_value(const Variant &p_data);
|
||||
void _accessibility_action_menu(const Variant &p_data);
|
||||
void _accessibility_scroll_down(const Variant &p_data);
|
||||
void _accessibility_scroll_left(const Variant &p_data);
|
||||
void _accessibility_scroll_right(const Variant &p_data);
|
||||
void _accessibility_scroll_up(const Variant &p_data);
|
||||
void _accessibility_scroll_set(const Variant &p_data);
|
||||
void _accessibility_action_scroll_into_view(const Variant &p_data, int p_line, int p_wrap);
|
||||
|
||||
GDVIRTUAL2(_handle_unicode_input, int, int)
|
||||
GDVIRTUAL1(_backspace, int)
|
||||
GDVIRTUAL1(_cut, int)
|
||||
|
|
@ -762,6 +798,9 @@ public:
|
|||
void set_indent_wrapped_lines(bool p_enabled);
|
||||
bool is_indent_wrapped_lines() const;
|
||||
|
||||
void set_tab_input_mode(bool p_enabled);
|
||||
bool get_tab_input_mode() const;
|
||||
|
||||
// User controls
|
||||
void set_overtype_mode_enabled(bool p_enabled);
|
||||
bool is_overtype_mode_enabled() const;
|
||||
|
|
@ -1138,5 +1177,3 @@ VARIANT_ENUM_CAST(TextEdit::SelectionMode);
|
|||
VARIANT_ENUM_CAST(TextEdit::GutterType);
|
||||
VARIANT_ENUM_CAST(TextEdit::MenuItems);
|
||||
VARIANT_ENUM_CAST(TextEdit::SearchFlags);
|
||||
|
||||
#endif // TEXT_EDIT_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TEXTURE_BUTTON_H
|
||||
#define TEXTURE_BUTTON_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/base_button.h"
|
||||
#include "scene/resources/bit_map.h"
|
||||
|
|
@ -104,5 +103,3 @@ public:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(TextureButton::StretchMode);
|
||||
|
||||
#endif // TEXTURE_BUTTON_H
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ Point2 TextureProgressBar::unit_val_to_uv(float val) {
|
|||
Point2 p = get_relative_center();
|
||||
|
||||
// Minimal version of Liang-Barsky clipping algorithm
|
||||
float angle = (val * Math_TAU) - Math_PI * 0.5;
|
||||
float angle = (val * Math::TAU) - Math::PI * 0.5;
|
||||
Point2 dir = Vector2(Math::cos(angle), Math::sin(angle));
|
||||
float t1 = 1.0;
|
||||
float cp = 0.0;
|
||||
|
|
@ -429,6 +429,13 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu
|
|||
|
||||
void TextureProgressBar::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_PROGRESS_INDICATOR);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
if (under.is_valid()) {
|
||||
if (nine_patch_stretch) {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TEXTURE_PROGRESS_BAR_H
|
||||
#define TEXTURE_PROGRESS_BAR_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/range.h"
|
||||
|
||||
|
|
@ -122,5 +121,3 @@ private:
|
|||
};
|
||||
|
||||
VARIANT_ENUM_CAST(TextureProgressBar::FillMode);
|
||||
|
||||
#endif // TEXTURE_PROGRESS_BAR_H
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TEXTURE_RECT_H
|
||||
#define TEXTURE_RECT_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
|
||||
|
|
@ -95,5 +94,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(TextureRect::ExpandMode);
|
||||
VARIANT_ENUM_CAST(TextureRect::StretchMode);
|
||||
|
||||
#endif // TEXTURE_RECT_H
|
||||
|
|
|
|||
41
engine/scene/gui/tree.compat.inc
Normal file
41
engine/scene/gui/tree.compat.inc
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**************************************************************************/
|
||||
/* tree.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 TreeItem::_add_button_bind_compat_76829(int p_column, const Ref<Texture2D> &p_button, int p_id, bool p_disabled, const String &p_tooltip) {
|
||||
add_button(p_column, p_button, p_id, p_disabled, p_tooltip, String());
|
||||
}
|
||||
|
||||
void TreeItem::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip_text"), &TreeItem::_add_button_bind_compat_76829, DEFVAL(-1), DEFVAL(false), DEFVAL(""));
|
||||
}
|
||||
|
||||
#endif // DISABLE_DEPRECATED
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef TREE_H
|
||||
#define TREE_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/line_edit.h"
|
||||
|
|
@ -46,17 +45,18 @@ class TreeItem : public Object {
|
|||
|
||||
public:
|
||||
enum TreeCellMode {
|
||||
CELL_MODE_STRING, ///< just a string
|
||||
CELL_MODE_CHECK, ///< string + check
|
||||
CELL_MODE_RANGE, ///< Contains a range
|
||||
CELL_MODE_ICON, ///< Contains an icon, not editable
|
||||
CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button
|
||||
CELL_MODE_STRING,
|
||||
CELL_MODE_CHECK,
|
||||
CELL_MODE_RANGE,
|
||||
CELL_MODE_ICON,
|
||||
CELL_MODE_CUSTOM,
|
||||
};
|
||||
|
||||
private:
|
||||
friend class Tree;
|
||||
|
||||
struct Cell {
|
||||
mutable RID accessibility_cell_element;
|
||||
TreeCellMode mode = TreeItem::CELL_MODE_STRING;
|
||||
|
||||
Ref<Texture2D> icon;
|
||||
|
|
@ -65,6 +65,7 @@ private:
|
|||
String text;
|
||||
String xl_text;
|
||||
Node::AutoTranslateMode auto_translate_mode = Node::AUTO_TRANSLATE_MODE_INHERIT;
|
||||
String alt_text;
|
||||
bool edit_multiline = false;
|
||||
String suffix;
|
||||
Ref<TextParagraph> text_buf;
|
||||
|
|
@ -105,11 +106,13 @@ private:
|
|||
Callable custom_draw_callback;
|
||||
|
||||
struct Button {
|
||||
mutable RID accessibility_button_element;
|
||||
int id = 0;
|
||||
bool disabled = false;
|
||||
Ref<Texture2D> texture;
|
||||
Color color = Color(1, 1, 1, 1);
|
||||
String tooltip;
|
||||
String alt_text;
|
||||
};
|
||||
|
||||
Vector<Button> buttons;
|
||||
|
|
@ -126,23 +129,26 @@ private:
|
|||
void draw_icon(const RID &p_where, const Point2 &p_pos, const Size2 &p_size = Size2(), const Color &p_color = Color()) const;
|
||||
};
|
||||
|
||||
mutable RID accessibility_row_element;
|
||||
mutable bool accessibility_row_dirty = true;
|
||||
|
||||
Vector<Cell> cells;
|
||||
|
||||
bool collapsed = false; // won't show children
|
||||
bool collapsed = false; // Won't show children.
|
||||
bool visible = true;
|
||||
bool parent_visible_in_tree = true;
|
||||
bool disable_folding = false;
|
||||
int custom_min_height = 0;
|
||||
|
||||
TreeItem *parent = nullptr; // parent item
|
||||
TreeItem *prev = nullptr; // previous in list
|
||||
TreeItem *next = nullptr; // next in list
|
||||
TreeItem *parent = nullptr; // Parent item.
|
||||
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
|
||||
Tree *tree = nullptr; // tree (for reference)
|
||||
bool is_root = false; // For tree root.
|
||||
Tree *tree = nullptr; // Tree (for reference).
|
||||
|
||||
TreeItem(Tree *p_tree);
|
||||
|
||||
|
|
@ -166,6 +172,22 @@ private:
|
|||
}
|
||||
|
||||
_FORCE_INLINE_ void _unlink_from_tree() {
|
||||
if (accessibility_row_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(accessibility_row_element);
|
||||
accessibility_row_element = RID();
|
||||
}
|
||||
for (Cell &cell : cells) {
|
||||
if (cell.accessibility_cell_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(cell.accessibility_cell_element);
|
||||
cell.accessibility_cell_element = RID();
|
||||
}
|
||||
for (Cell::Button &btn : cell.buttons) {
|
||||
if (btn.accessibility_button_element.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(btn.accessibility_button_element);
|
||||
btn.accessibility_button_element = RID();
|
||||
}
|
||||
}
|
||||
}
|
||||
TreeItem *p = get_prev();
|
||||
if (p) {
|
||||
p->next = next;
|
||||
|
|
@ -191,7 +213,12 @@ private:
|
|||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
// Bind helpers
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _add_button_bind_compat_76829(int p_column, const Ref<Texture2D> &p_button, int p_id, bool p_disabled, const String &p_tooltip);
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
// Bind helpers.
|
||||
Dictionary _get_range_config(int p_column) {
|
||||
Dictionary d;
|
||||
double min = 0.0, max = 0.0, step = 0.0;
|
||||
|
|
@ -207,19 +234,19 @@ protected:
|
|||
void _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
|
||||
public:
|
||||
/* cell mode */
|
||||
// Cell mode.
|
||||
void set_cell_mode(int p_column, TreeCellMode p_mode);
|
||||
TreeCellMode get_cell_mode(int p_column) const;
|
||||
|
||||
/* auto translate mode */
|
||||
// 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 */
|
||||
// Multiline editable.
|
||||
void set_edit_multiline(int p_column, bool p_multiline);
|
||||
bool is_edit_multiline(int p_column) const;
|
||||
|
||||
/* check mode */
|
||||
// Check mode.
|
||||
void set_checked(int p_column, bool p_checked);
|
||||
void set_indeterminate(int p_column, bool p_indeterminate);
|
||||
bool is_checked(int p_column) const;
|
||||
|
|
@ -241,6 +268,9 @@ public:
|
|||
void set_text(int p_column, String p_text);
|
||||
String get_text(int p_column) const;
|
||||
|
||||
void set_alt_text(int p_column, String p_text);
|
||||
String get_alt_text(int p_column) const;
|
||||
|
||||
void set_text_direction(int p_column, Control::TextDirection p_text_direction);
|
||||
Control::TextDirection get_text_direction(int p_column) const;
|
||||
|
||||
|
|
@ -278,7 +308,7 @@ public:
|
|||
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 = "");
|
||||
void add_button(int p_column, const Ref<Texture2D> &p_button, int p_id = -1, bool p_disabled = false, const String &p_tooltip = "", const String &p_alt_text = "");
|
||||
int get_button_count(int p_column) const;
|
||||
String get_button_tooltip_text(int p_column, int p_index) const;
|
||||
Ref<Texture2D> get_button(int p_column, int p_index) const;
|
||||
|
|
@ -288,12 +318,12 @@ public:
|
|||
Color get_button_color(int p_column, int p_index) const;
|
||||
void set_button_tooltip_text(int p_column, int p_index, const String &p_tooltip);
|
||||
void set_button(int p_column, int p_index, const Ref<Texture2D> &p_button);
|
||||
void set_button_alt_text(int p_column, int p_index, const String &p_alt_text);
|
||||
void set_button_color(int p_column, int p_index, const Color &p_color);
|
||||
void set_button_disabled(int p_column, int p_index, bool p_disabled);
|
||||
bool is_button_disabled(int p_column, int p_index) const;
|
||||
|
||||
/* range works for mode number or mode combo */
|
||||
|
||||
// Range works for mode number or mode combo.
|
||||
void set_range(int p_column, double p_value);
|
||||
double get_range(int p_column) const;
|
||||
|
||||
|
|
@ -367,8 +397,7 @@ public:
|
|||
|
||||
Size2 get_minimum_size(int p_column);
|
||||
|
||||
/* Item manipulation */
|
||||
|
||||
// Item manipulation.
|
||||
TreeItem *create_child(int p_index = -1);
|
||||
void add_child(TreeItem *p_item);
|
||||
void remove_child(TreeItem *p_item);
|
||||
|
|
@ -460,12 +489,10 @@ private:
|
|||
|
||||
bool propagate_mouse_activated = false;
|
||||
|
||||
//TreeItem *cursor_item;
|
||||
//int cursor_column;
|
||||
|
||||
Rect2 custom_popup_rect;
|
||||
int edited_col = -1;
|
||||
int selected_col = -1;
|
||||
int selected_button = -1;
|
||||
int popup_edited_item_col = -1;
|
||||
bool hide_root = false;
|
||||
SelectMode select_mode = SELECT_SINGLE;
|
||||
|
|
@ -475,6 +502,7 @@ private:
|
|||
int drop_mode_flags = 0;
|
||||
|
||||
struct ColumnInfo {
|
||||
mutable RID accessibility_col_element;
|
||||
int custom_min_width = 0;
|
||||
int expand_ratio = 1;
|
||||
bool expand = true;
|
||||
|
|
@ -499,6 +527,8 @@ private:
|
|||
VBoxContainer *popup_editor_vb = nullptr;
|
||||
|
||||
bool popup_edit_committed = true;
|
||||
RID accessibility_scroll_element;
|
||||
|
||||
Popup *popup_editor = nullptr;
|
||||
LineEdit *line_editor = nullptr;
|
||||
TextEdit *text_editor = nullptr;
|
||||
|
|
@ -520,7 +550,6 @@ private:
|
|||
void update_column(int p_col);
|
||||
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);
|
||||
void select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev = nullptr, bool *r_in_range = nullptr, bool p_force_deselect = false);
|
||||
|
|
@ -551,6 +580,8 @@ private:
|
|||
|
||||
Ref<StyleBox> hovered;
|
||||
Ref<StyleBox> hovered_dimmed;
|
||||
Ref<StyleBox> hovered_selected;
|
||||
Ref<StyleBox> hovered_selected_focus;
|
||||
Ref<StyleBox> selected;
|
||||
Ref<StyleBox> selected_focus;
|
||||
Ref<StyleBox> cursor;
|
||||
|
|
@ -581,6 +612,7 @@ private:
|
|||
Color font_color;
|
||||
Color font_hovered_color;
|
||||
Color font_hovered_dimmed_color;
|
||||
Color font_hovered_selected_color;
|
||||
Color font_selected_color;
|
||||
Color font_disabled_color;
|
||||
Color guide_color;
|
||||
|
|
@ -628,7 +660,6 @@ private:
|
|||
CLICK_NONE,
|
||||
CLICK_TITLE,
|
||||
CLICK_BUTTON,
|
||||
|
||||
};
|
||||
|
||||
ClickType click_type = Cache::CLICK_NONE;
|
||||
|
|
@ -661,7 +692,6 @@ private:
|
|||
void update_scrollbars();
|
||||
|
||||
Rect2 search_item_rect(TreeItem *p_from, TreeItem *p_item);
|
||||
//Rect2 get_item_rect(TreeItem *p_item);
|
||||
uint64_t last_keypress = 0;
|
||||
String incr_search;
|
||||
bool cursor_can_exit_tree = true;
|
||||
|
|
@ -683,17 +713,9 @@ private:
|
|||
|
||||
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;
|
||||
|
||||
float last_drag_accum;
|
||||
float last_drag_time;
|
||||
float time_since_motion;*/
|
||||
|
||||
float drag_speed = 0.0;
|
||||
float drag_from = 0.0;
|
||||
float drag_accum = 0.0;
|
||||
Vector2 last_speed;
|
||||
bool drag_touching = false;
|
||||
bool drag_touching_deaccel = false;
|
||||
bool click_handled = false;
|
||||
|
|
@ -727,13 +749,39 @@ private:
|
|||
Rect2 _get_content_rect() const; // Considering the background stylebox and scrollbars.
|
||||
Rect2 _get_item_focus_rect(const TreeItem *p_item) const;
|
||||
|
||||
void _check_item_accessibility(TreeItem *p_item, PackedStringArray &r_warnings, int &r_row) const;
|
||||
|
||||
void _accessibility_clean_info(TreeItem *p_item);
|
||||
void _accessibility_update_item(Point2 &r_ofs, TreeItem *p_item, int &r_row, int p_level);
|
||||
|
||||
protected:
|
||||
virtual void _update_theme_item_cache() override;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
void _accessibility_action_scroll_down(const Variant &p_data);
|
||||
void _accessibility_action_scroll_left(const Variant &p_data);
|
||||
void _accessibility_action_scroll_right(const Variant &p_data);
|
||||
void _accessibility_action_scroll_up(const Variant &p_data);
|
||||
void _accessibility_action_scroll_set(const Variant &p_data);
|
||||
void _accessibility_action_scroll_into_view(const Variant &p_data, TreeItem *p_item, int p_col);
|
||||
void _accessibility_action_focus(const Variant &p_data, TreeItem *p_item, int p_col);
|
||||
void _accessibility_action_blur(const Variant &p_data, TreeItem *p_item, int p_col);
|
||||
void _accessibility_action_collapse(const Variant &p_data, TreeItem *p_item);
|
||||
void _accessibility_action_expand(const Variant &p_data, TreeItem *p_item);
|
||||
void _accessibility_action_set_text_value(const Variant &p_data, TreeItem *p_item, int p_col);
|
||||
void _accessibility_action_set_num_value(const Variant &p_data, TreeItem *p_item, int p_col);
|
||||
void _accessibility_action_set_bool_value(const Variant &p_data, TreeItem *p_item, int p_col);
|
||||
void _accessibility_action_set_inc(const Variant &p_data, TreeItem *p_item, int p_col);
|
||||
void _accessibility_action_set_dec(const Variant &p_data, TreeItem *p_item, int p_col);
|
||||
void _accessibility_action_edit_custom(const Variant &p_data, TreeItem *p_item, int p_col);
|
||||
void _accessibility_action_button_press(const Variant &p_data, TreeItem *p_item, int p_col, int p_btn);
|
||||
|
||||
public:
|
||||
PackedStringArray get_accessibility_configuration_warnings() const override;
|
||||
virtual RID get_focused_accessibility_element() const override;
|
||||
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
||||
virtual String get_tooltip(const Point2 &p_pos) const override;
|
||||
|
|
@ -854,5 +902,3 @@ public:
|
|||
|
||||
VARIANT_ENUM_CAST(Tree::SelectMode);
|
||||
VARIANT_ENUM_CAST(Tree::DropModeFlags);
|
||||
|
||||
#endif // TREE_H
|
||||
|
|
|
|||
|
|
@ -127,6 +127,13 @@ void VideoStreamPlayer::_mix_audio() {
|
|||
|
||||
void VideoStreamPlayer::_notification(int p_notification) {
|
||||
switch (p_notification) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_VIDEO);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
AudioServer::get_singleton()->add_mix_callback(_mix_audios, this);
|
||||
|
||||
|
|
@ -136,6 +143,7 @@ void VideoStreamPlayer::_notification(int p_notification) {
|
|||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
stop();
|
||||
AudioServer::get_singleton()->remove_mix_callback(_mix_audios, this);
|
||||
} break;
|
||||
|
||||
|
|
@ -175,7 +183,7 @@ void VideoStreamPlayer::_notification(int p_notification) {
|
|||
return;
|
||||
}
|
||||
|
||||
Size2 s = expand ? get_size() : texture->get_size();
|
||||
Size2 s = expand ? get_size() : texture_size;
|
||||
draw_texture_rect(texture, Rect2(Point2(), s), false);
|
||||
} break;
|
||||
|
||||
|
|
@ -211,9 +219,25 @@ void VideoStreamPlayer::_notification(int p_notification) {
|
|||
}
|
||||
}
|
||||
|
||||
void VideoStreamPlayer::texture_changed(const Ref<Texture2D> &p_texture) {
|
||||
const Size2 new_texture_size = p_texture.is_valid() ? p_texture->get_size() : Size2();
|
||||
|
||||
if (new_texture_size == texture_size) {
|
||||
return;
|
||||
}
|
||||
|
||||
texture_size = new_texture_size;
|
||||
|
||||
queue_redraw();
|
||||
|
||||
if (!expand) {
|
||||
update_minimum_size();
|
||||
}
|
||||
}
|
||||
|
||||
Size2 VideoStreamPlayer::get_minimum_size() const {
|
||||
if (!expand && texture.is_valid()) {
|
||||
return texture->get_size();
|
||||
return texture_size;
|
||||
} else {
|
||||
return Size2();
|
||||
}
|
||||
|
|
@ -265,10 +289,19 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
|
|||
stream->connect_changed(callable_mp(this, &VideoStreamPlayer::set_stream).bind(stream));
|
||||
}
|
||||
|
||||
if (texture.is_valid()) {
|
||||
texture->disconnect_changed(callable_mp(this, &VideoStreamPlayer::texture_changed));
|
||||
}
|
||||
|
||||
if (playback.is_valid()) {
|
||||
playback->set_paused(paused);
|
||||
texture = playback->get_texture();
|
||||
|
||||
if (texture.is_valid()) {
|
||||
texture_size = texture->get_size();
|
||||
texture->connect_changed(callable_mp(this, &VideoStreamPlayer::texture_changed).bind(texture));
|
||||
}
|
||||
|
||||
const int channels = playback->get_channels();
|
||||
|
||||
AudioServer::get_singleton()->lock();
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@
|
|||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef VIDEO_STREAM_PLAYER_H
|
||||
#define VIDEO_STREAM_PLAYER_H
|
||||
#pragma once
|
||||
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/resources/video_stream.h"
|
||||
|
|
@ -52,6 +51,8 @@ class VideoStreamPlayer : public Control {
|
|||
RID stream_rid;
|
||||
|
||||
Ref<Texture2D> texture;
|
||||
Size2 texture_size;
|
||||
void texture_changed(const Ref<Texture2D> &p_texture);
|
||||
|
||||
AudioRBResampler resampler;
|
||||
Vector<AudioFrame> mix_buffer;
|
||||
|
|
@ -126,5 +127,3 @@ public:
|
|||
VideoStreamPlayer();
|
||||
~VideoStreamPlayer();
|
||||
};
|
||||
|
||||
#endif // VIDEO_STREAM_PLAYER_H
|
||||
|
|
|
|||
|
|
@ -226,9 +226,7 @@ void ViewPanner::set_force_drag(bool p_force) {
|
|||
}
|
||||
|
||||
ViewPanner::ViewPanner() {
|
||||
Array inputs;
|
||||
inputs.append(InputEventKey::create_reference(Key::SPACE));
|
||||
|
||||
Array inputs = { InputEventKey::create_reference(Key::SPACE) };
|
||||
pan_view_shortcut.instantiate();
|
||||
pan_view_shortcut->set_events(inputs);
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue