mirror of
https://github.com/godotengine/godot.git
synced 2025-04-25 01:48:08 +08:00
Base accessibility API.
This commit is contained in:
parent
af2c713971
commit
b106dfd4f9
.pre-commit-config.yaml
core
doc/classes
Control.xmlDisplayServer.xmlGraphEdit.xmlGraphNode.xmlInputMap.xmlLabel.xmlLinkButton.xmlMenuBar.xmlMenuButton.xmlNode.xmlProjectSettings.xmlRichTextLabel.xmlSceneTree.xmlScrollBar.xmlTextEdit.xmlTextLine.xmlTextParagraph.xmlTextServer.xmlTextServerExtension.xmlTreeItem.xmlViewport.xmlWindow.xml
main
misc/extension_api_validation
modules
scene
2d
3d
audio
gui
base_button.cppbase_button.hbutton.cppcheck_box.cppcheck_box.hcheck_button.cppcolor_picker.cppcolor_rect.cppcontainer.cppcontrol.cppcontrol.hdialogs.cppfile_dialog.cppgraph_edit.cppgraph_edit.hgraph_node.cppgraph_node.hitem_list.cppitem_list.hlabel.cpplabel.hline_edit.cppline_edit.hlink_button.cppmenu_bar.cppmenu_button.cppoption_button.cpppanel.cpppopup.cpppopup.hpopup_menu.cpppopup_menu.hprogress_bar.cpprange.cpprange.hrich_text_label.compat.incrich_text_label.cpprich_text_label.hscroll_bar.cppscroll_container.cppscroll_container.hslider.cppspin_box.cppspin_box.hsplit_container.cppsplit_container.htab_bar.cpptab_bar.htab_container.cpptab_container.htext_edit.cpptext_edit.htexture_progress_bar.cpptree.compat.inc
@ -5,6 +5,8 @@ exclude: |
|
||||
(?x)^(
|
||||
.*thirdparty/.*|
|
||||
.*-so_wrap\.(h|c)|
|
||||
.*-dll_wrap\.(h|c)|
|
||||
.*-dylib_wrap\.(h|c)|
|
||||
platform/android/java/editor/src/main/java/com/android/.*|
|
||||
platform/android/java/lib/src/com/google/.*
|
||||
)$
|
||||
|
@ -1491,6 +1491,9 @@ ProjectSettings::ProjectSettings() {
|
||||
GLOBAL_DEF("application/config/auto_accept_quit", true);
|
||||
GLOBAL_DEF("application/config/quit_on_go_back", true);
|
||||
|
||||
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "accessibility/general/accessibility_support", PROPERTY_HINT_ENUM, "Auto (When Screen Reader is Running),Always Active,Disabled"), 0);
|
||||
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "accessibility/general/updates_per_second", PROPERTY_HINT_RANGE, "1,100,1"), 60);
|
||||
|
||||
// The default window size is tuned to:
|
||||
// - Have a 16:9 aspect ratio,
|
||||
// - Have both dimensions divisible by 8 to better play along with video recording,
|
||||
|
@ -47,6 +47,8 @@ void InputMap::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(DEFAULT_DEADZONE));
|
||||
ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_action_description", "action"), &InputMap::get_action_description);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone);
|
||||
ClassDB::bind_method(D_METHOD("action_get_deadzone", "action"), &InputMap::action_get_deadzone);
|
||||
ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event);
|
||||
@ -181,6 +183,25 @@ bool InputMap::has_action(const StringName &p_action) const {
|
||||
return input_map.has(p_action);
|
||||
}
|
||||
|
||||
String InputMap::get_action_description(const StringName &p_action) const {
|
||||
ERR_FAIL_COND_V_MSG(!input_map.has(p_action), String(), suggest_actions(p_action));
|
||||
|
||||
String ret;
|
||||
const List<Ref<InputEvent>> &inputs = input_map[p_action].inputs;
|
||||
for (Ref<InputEventKey> iek : inputs) {
|
||||
if (iek.is_valid()) {
|
||||
if (!ret.is_empty()) {
|
||||
ret += RTR(" or ");
|
||||
}
|
||||
ret += iek->as_text();
|
||||
}
|
||||
}
|
||||
if (ret.is_empty()) {
|
||||
ret = RTR("Action has no bound inputs");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
float InputMap::action_get_deadzone(const StringName &p_action) {
|
||||
ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, suggest_actions(p_action));
|
||||
|
||||
@ -344,6 +365,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
|
||||
{ "ui_cut", TTRC("Cut") },
|
||||
{ "ui_copy", TTRC("Copy") },
|
||||
{ "ui_paste", TTRC("Paste") },
|
||||
{ "ui_focus_mode", TTRC("Toggle Tab Focus Mode") },
|
||||
{ "ui_undo", TTRC("Undo") },
|
||||
{ "ui_redo", TTRC("Redo") },
|
||||
{ "ui_text_completion_query", TTRC("Completion Query") },
|
||||
@ -397,12 +419,15 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = {
|
||||
{ "ui_text_submit", TTRC("Submit Text") },
|
||||
{ "ui_graph_duplicate", TTRC("Duplicate Nodes") },
|
||||
{ "ui_graph_delete", TTRC("Delete Nodes") },
|
||||
{ "ui_graph_follow_left", TTRC("Follow Input Port Connection") },
|
||||
{ "ui_graph_follow_right", TTRC("Follow Output Port Connection") },
|
||||
{ "ui_filedialog_up_one_level", TTRC("Go Up One Level") },
|
||||
{ "ui_filedialog_refresh", TTRC("Refresh") },
|
||||
{ "ui_filedialog_show_hidden", TTRC("Show Hidden") },
|
||||
{ "ui_swap_input_direction ", TTRC("Swap Input Direction") },
|
||||
{ "ui_unicode_start", TTRC("Start Unicode Character Input") },
|
||||
{ "ui_colorpicker_delete_preset", TTRC("Toggle License Notices") },
|
||||
{ "ui_colorpicker_delete_preset", TTRC("Toggle License Notices") },
|
||||
{ "ui_accessibility_drag_and_drop", TTRC("Accessibility: Keyboard Drag and Drop") },
|
||||
{ "", ""}
|
||||
/* clang-format on */
|
||||
};
|
||||
@ -488,6 +513,9 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
|
||||
inputs.push_back(InputEventKey::create_reference(Key::END));
|
||||
default_builtin_cache.insert("ui_end", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
default_builtin_cache.insert("ui_accessibility_drag_and_drop", inputs);
|
||||
|
||||
// ///// UI basic Shortcuts /////
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
@ -500,6 +528,10 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
|
||||
inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::CMD_OR_CTRL));
|
||||
default_builtin_cache.insert("ui_copy", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::M | KeyModifierMask::CTRL));
|
||||
default_builtin_cache.insert("ui_focus_mode", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::V | KeyModifierMask::CMD_OR_CTRL));
|
||||
inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::SHIFT));
|
||||
@ -773,6 +805,22 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() {
|
||||
inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE));
|
||||
default_builtin_cache.insert("ui_graph_delete", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL));
|
||||
default_builtin_cache.insert("ui_graph_follow_left", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::ALT));
|
||||
default_builtin_cache.insert("ui_graph_follow_left.macos", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL));
|
||||
default_builtin_cache.insert("ui_graph_follow_right", inputs);
|
||||
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::ALT));
|
||||
default_builtin_cache.insert("ui_graph_follow_right.macos", inputs);
|
||||
|
||||
// ///// UI File Dialog Shortcuts /////
|
||||
inputs = List<Ref<InputEvent>>();
|
||||
inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE));
|
||||
|
@ -85,6 +85,8 @@ public:
|
||||
void add_action(const StringName &p_action, float p_deadzone = DEFAULT_DEADZONE);
|
||||
void erase_action(const StringName &p_action);
|
||||
|
||||
String get_action_description(const StringName &p_action) const;
|
||||
|
||||
float action_get_deadzone(const StringName &p_action);
|
||||
void action_set_deadzone(const StringName &p_action, float p_deadzone);
|
||||
void action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event);
|
||||
|
@ -2073,34 +2073,45 @@ Error String::append_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
CharString String::utf8() const {
|
||||
CharString String::utf8(Vector<uint8_t> *r_ch_length_map) const {
|
||||
int l = length();
|
||||
if (!l) {
|
||||
return CharString();
|
||||
}
|
||||
|
||||
uint8_t *map_ptr = nullptr;
|
||||
if (r_ch_length_map) {
|
||||
r_ch_length_map->resize(l);
|
||||
map_ptr = r_ch_length_map->ptrw();
|
||||
}
|
||||
|
||||
const char32_t *d = &operator[](0);
|
||||
int fl = 0;
|
||||
for (int i = 0; i < l; i++) {
|
||||
uint32_t c = d[i];
|
||||
int ch_w = 1;
|
||||
if (c <= 0x7f) { // 7 bits.
|
||||
fl += 1;
|
||||
ch_w = 1;
|
||||
} else if (c <= 0x7ff) { // 11 bits
|
||||
fl += 2;
|
||||
ch_w = 2;
|
||||
} else if (c <= 0xffff) { // 16 bits
|
||||
fl += 3;
|
||||
ch_w = 3;
|
||||
} else if (c <= 0x001fffff) { // 21 bits
|
||||
fl += 4;
|
||||
ch_w = 4;
|
||||
} else if (c <= 0x03ffffff) { // 26 bits
|
||||
fl += 5;
|
||||
ch_w = 5;
|
||||
print_unicode_error(vformat("Invalid unicode codepoint (%x)", c));
|
||||
} else if (c <= 0x7fffffff) { // 31 bits
|
||||
fl += 6;
|
||||
ch_w = 6;
|
||||
print_unicode_error(vformat("Invalid unicode codepoint (%x)", c));
|
||||
} else {
|
||||
fl += 1;
|
||||
ch_w = 1;
|
||||
print_unicode_error(vformat("Invalid unicode codepoint (%x), cannot represent as UTF-8", c), true);
|
||||
}
|
||||
fl += ch_w;
|
||||
if (map_ptr) {
|
||||
map_ptr[i] = ch_w;
|
||||
}
|
||||
}
|
||||
|
||||
CharString utf8s;
|
||||
|
@ -511,7 +511,7 @@ public:
|
||||
return string;
|
||||
}
|
||||
|
||||
CharString utf8() const;
|
||||
CharString utf8(Vector<uint8_t> *r_ch_length_map = nullptr) const;
|
||||
Error append_utf8(const char *p_utf8, int p_len = -1, bool p_skip_cr = false);
|
||||
Error append_utf8(const Span<char> &p_range, bool p_skip_cr = false) {
|
||||
return append_utf8(p_range.ptr(), p_range.size(), p_skip_cr);
|
||||
|
@ -24,6 +24,12 @@
|
||||
<link title="All GUI Demos">https://github.com/godotengine/godot-demo-projects/tree/master/gui</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="_accessibility_get_contextual_info" qualifiers="virtual const">
|
||||
<return type="String" />
|
||||
<description>
|
||||
Return the description of the keyboard shortcuts and other contextual help for this control.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_can_drop_data" qualifiers="virtual const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="at_position" type="Vector2" />
|
||||
@ -31,6 +37,7 @@
|
||||
<description>
|
||||
Godot calls this method to test if [param data] from a control's [method _get_drag_data] can be dropped at [param at_position]. [param at_position] is local to this control.
|
||||
This method should only be used to test the data. Process the data in [method _drop_data].
|
||||
[b]Note:[/b] If drag was initiated by keyboard shortcut or [method accessibility_drag], [param at_position] is set to [code]Vector2(INFINITY, INFINITY)[/code] and the currently selected item/text position should be used as drop position.
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
func _can_drop_data(position, data):
|
||||
@ -55,6 +62,7 @@
|
||||
<param index="1" name="data" type="Variant" />
|
||||
<description>
|
||||
Godot calls this method to pass you the [param data] from a control's [method _get_drag_data] result. Godot first calls [method _can_drop_data] to test if [param data] is allowed to drop at [param at_position] where [param at_position] is local to this control.
|
||||
[b]Note:[/b] If drag was initiated by keyboard shortcut or [method accessibility_drag], [param at_position] is set to [code]Vector2(INFINITY, INFINITY)[/code] and the currently selected item/text position should be used as drop position.
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
func _can_drop_data(position, data):
|
||||
@ -83,6 +91,7 @@
|
||||
<description>
|
||||
Godot calls this method to get data that can be dragged and dropped onto controls that expect drop data. Returns [code]null[/code] if there is no data to drag. Controls that want to receive drop data should implement [method _can_drop_data] and [method _drop_data]. [param at_position] is local to this control. Drag may be forced with [method force_drag].
|
||||
A preview that will follow the mouse that should represent the data can be set with [method set_drag_preview]. A good time to set the preview is in this method.
|
||||
[b]Note:[/b] If drag was initiated by keyboard shortcut or [method accessibility_drag], [param at_position] is set to [code]Vector2(INFINITY, INFINITY)[/code] and the currently selected item/text position should be used as drop position.
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
func _get_drag_data(position):
|
||||
@ -223,6 +232,18 @@
|
||||
[b]Note:[/b] This does not affect the methods in [Input], only the way events are propagated.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_drag">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Starts drag-and-drop operation without using a mouse.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_drop">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Ends drag-and-drop operation without using a mouse.
|
||||
</description>
|
||||
</method>
|
||||
<method name="add_theme_color_override">
|
||||
<return type="void" />
|
||||
<param index="0" name="name" type="StringName" />
|
||||
@ -1174,6 +1195,9 @@
|
||||
<constant name="FOCUS_ALL" value="2" enum="FocusMode">
|
||||
The node can grab focus on mouse click, using the arrows and the Tab keys on the keyboard, or using the D-pad buttons on a gamepad. Use with [member focus_mode].
|
||||
</constant>
|
||||
<constant name="FOCUS_ACCESSIBILITY" value="3" enum="FocusMode">
|
||||
The node can grab focus only when screen reader is active. Use with [member focus_mode].
|
||||
</constant>
|
||||
<constant name="RECURSIVE_BEHAVIOR_INHERITED" value="0" enum="RecursiveBehavior">
|
||||
Inherits the associated behavior from the control's parent. This is the default for any newly created control.
|
||||
</constant>
|
||||
|
@ -10,6 +10,630 @@
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="accessibility_create_element">
|
||||
<return type="RID" />
|
||||
<param index="0" name="window_id" type="int" />
|
||||
<param index="1" name="role" type="int" enum="DisplayServer.AccessibilityRole" />
|
||||
<description>
|
||||
Creates a new, empty accessibility element resource.
|
||||
[b]Note:[/b] An accessibility element is created and freed automatically for each [Node]. In general, this function should not be called manually.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_create_sub_element">
|
||||
<return type="RID" />
|
||||
<param index="0" name="parent_rid" type="RID" />
|
||||
<param index="1" name="role" type="int" enum="DisplayServer.AccessibilityRole" />
|
||||
<param index="2" name="insert_pos" type="int" default="-1" />
|
||||
<description>
|
||||
Creates a new, empty accessibility sub-element resource. Sub-elements can be used to provide accessibility information for objects which are not [Node]s, such as list items, table cells, or menu items. Sub-elements are freed automatically when the parent element is freed, or can be freed early using the [method accessibility_free_element] method.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_create_sub_text_edit_elements">
|
||||
<return type="RID" />
|
||||
<param index="0" name="parent_rid" type="RID" />
|
||||
<param index="1" name="shaped_text" type="RID" />
|
||||
<param index="2" name="min_height" type="float" />
|
||||
<param index="3" name="insert_pos" type="int" default="-1" />
|
||||
<description>
|
||||
Creates a new, empty accessibility sub-element from the shaped text buffer. Sub-elements are freed automatically when the parent element is freed, or can be freed early using the [method accessibility_free_element] method.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_element_get_meta" qualifiers="const">
|
||||
<return type="Variant" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<description>
|
||||
Returns the metadata of the accessibility element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_element_set_meta">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="meta" type="Variant" />
|
||||
<description>
|
||||
Sets the metadata of the accessibility element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_free_element">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<description>
|
||||
Frees an object created by [method accessibility_create_element], [method accessibility_create_sub_element], or [method accessibility_create_sub_text_edit_elements].
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_get_window_root" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<param index="0" name="window_id" type="int" />
|
||||
<description>
|
||||
Returns the main accessibility element of the OS native window.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_has_element" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<description>
|
||||
Returns [code]true[/code] if [param id] is a valid accessibility element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_screen_reader_active" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns [code]1[/code] if a screen reader, Braille display or other assistive app is active, [code]0[/code] otherwise. Returns [code]-1[/code] if status is unknown.
|
||||
[b]Note:[/b] This method is implemented on Linux, macOS, and Windows.
|
||||
[b]Note:[/b] Accessibility debugging tools, such as Accessibility Insights for Windows, macOS Accessibility Inspector, or AT-SPI Browser do not count as assistive apps and will not affect this value. To test your app with these tools, set [member ProjectSettings.accessibility/general/accessibility_support] to [code]1[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_set_window_focused">
|
||||
<return type="void" />
|
||||
<param index="0" name="window_id" type="int" />
|
||||
<param index="1" name="focused" type="bool" />
|
||||
<description>
|
||||
Sets the window focused state for assistive apps.
|
||||
[b]Note:[/b] This method is implemented on Linux, macOS, and Windows.
|
||||
[b]Note:[/b] Advanced users only! [Window] objects call this method automatically.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_set_window_rect">
|
||||
<return type="void" />
|
||||
<param index="0" name="window_id" type="int" />
|
||||
<param index="1" name="rect_out" type="Rect2" />
|
||||
<param index="2" name="rect_in" type="Rect2" />
|
||||
<description>
|
||||
Sets window outer (with decorations) and inner (without decorations) bounds for assistive apps.
|
||||
[b]Note:[/b] This method is implemented on Linux, macOS, and Windows.
|
||||
[b]Note:[/b] Advanced users only! [Window] objects call this method automatically.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_should_increase_contrast" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns [code]1[/code] if a high-contrast user interface theme should be used, [code]0[/code] otherwise. Returns [code]-1[/code] if status is unknown.
|
||||
[b]Note:[/b] This method is implemented on Linux (X11/Wayland, GNOME), macOS, and Windows.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_should_reduce_animation" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns [code]1[/code] if flashing, blinking, and other moving content that can cause seizures in users with photosensitive epilepsy should be disabled, [code]0[/code] otherwise. Returns [code]-1[/code] if status is unknown.
|
||||
[b]Note:[/b] This method is implemented on macOS and Windows.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_should_reduce_transparency" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
Returns [code]1[/code] if background images, transparency, and other features that can reduce the contrast between the foreground and background should be disabled, [code]0[/code] otherwise. Returns [code]-1[/code] if status is unknown.
|
||||
[b]Note:[/b] This method is implemented on macOS and Windows.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_add_action">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="action" type="int" enum="DisplayServer.AccessibilityAction" />
|
||||
<param index="2" name="callable" type="Callable" />
|
||||
<description>
|
||||
Adds a callback for the accessibility action (action which can be performed by using a special screen reader command or buttons on the Braille display), and marks this action as supported. The action callback receives one [Variant] argument, which value depends on action type.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_add_child">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="child_id" type="RID" />
|
||||
<description>
|
||||
Adds a child accessibility element.
|
||||
[b]Note:[/b] [Node] children and sub-elements are added to the child list automatically.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_add_custom_action">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="action_id" type="int" />
|
||||
<param index="2" name="action_description" type="String" />
|
||||
<description>
|
||||
Adds support for a custom accessibility action. [param action_id] is passed as an argument to the callback of [constant ACTION_CUSTOM] action.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_add_related_controls">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="related_id" type="RID" />
|
||||
<description>
|
||||
Adds an element that is controlled by this element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_add_related_described_by">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="related_id" type="RID" />
|
||||
<description>
|
||||
Adds an element that describes this element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_add_related_details">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="related_id" type="RID" />
|
||||
<description>
|
||||
Adds an element that details this element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_add_related_flow_to">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="related_id" type="RID" />
|
||||
<description>
|
||||
Adds an element that this element flow into.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_add_related_labeled_by">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="related_id" type="RID" />
|
||||
<description>
|
||||
Adds an element that labels this element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_add_related_radio_group">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="related_id" type="RID" />
|
||||
<description>
|
||||
Adds an element that is part of the same radio group.
|
||||
[b]Note:[/b] This method should be called on each element of the group, using all other elements as [param related_id].
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_active_descendant">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="other_id" type="RID" />
|
||||
<description>
|
||||
Adds an element that is an active descendant of this element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_background_color">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="color" type="Color" />
|
||||
<description>
|
||||
Sets element background color.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_bounds">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="p_rect" type="Rect2" />
|
||||
<description>
|
||||
Sets element bounding box, relative to the node position.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_checked">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="checekd" type="bool" />
|
||||
<description>
|
||||
Sets element checked state.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_classname">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="classname" type="String" />
|
||||
<description>
|
||||
Sets element class name.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_color_value">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="color" type="Color" />
|
||||
<description>
|
||||
Sets element color value.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_description">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="description" type="String" />
|
||||
<description>
|
||||
Sets element accessibility description.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_error_message">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="other_id" type="RID" />
|
||||
<description>
|
||||
Sets an element which contains an error message for this element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_extra_info">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="name" type="String" />
|
||||
<description>
|
||||
Sets element accessibility extra information added to the element name.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_flag">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="flag" type="int" enum="DisplayServer.AccessibilityFlags" />
|
||||
<param index="2" name="value" type="bool" />
|
||||
<description>
|
||||
Sets element flag, see [enum DisplayServer.AccessibilityFlags].
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_focus">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<description>
|
||||
Sets currently focused element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_foreground_color">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="color" type="Color" />
|
||||
<description>
|
||||
Sets element foreground color.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_in_page_link_target">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="other_id" type="RID" />
|
||||
<description>
|
||||
Sets target element for the link.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_language">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="language" type="String" />
|
||||
<description>
|
||||
Sets element text language.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_list_item_count">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="size" type="int" />
|
||||
<description>
|
||||
Sets number of items in the list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_list_item_expanded">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="expanded" type="bool" />
|
||||
<description>
|
||||
Sets list/tree item expanded status.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_list_item_index">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Sets the position of the element in the list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_list_item_level">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="level" type="int" />
|
||||
<description>
|
||||
Sets the hierarchical level of the element in the list.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_list_item_selected">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="selected" type="bool" />
|
||||
<description>
|
||||
Sets list/tree item selected status.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_list_orientation">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="vertical" type="bool" />
|
||||
<description>
|
||||
Sets the orientation of the list elements.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_live">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="live" type="int" enum="DisplayServer.AccessibilityLiveMode" />
|
||||
<description>
|
||||
Sets the priority of the live region updates.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_member_of">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="group_id" type="RID" />
|
||||
<description>
|
||||
Sets the element to be a member of the group.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_name">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="name" type="String" />
|
||||
<description>
|
||||
Sets element accessibility name.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_next_on_line">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="other_id" type="RID" />
|
||||
<description>
|
||||
Sets next element on the line.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_num_jump">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="jump" type="float" />
|
||||
<description>
|
||||
Sets numeric value jump.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_num_range">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="min" type="float" />
|
||||
<param index="2" name="max" type="float" />
|
||||
<description>
|
||||
Sets numeric value range.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_num_step">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="step" type="float" />
|
||||
<description>
|
||||
Sets numeric value step.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_num_value">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="position" type="float" />
|
||||
<description>
|
||||
Sets numeric value.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_placeholder">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="placeholder" type="String" />
|
||||
<description>
|
||||
Sets placeholder text.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_popup_type">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="popup" type="int" enum="DisplayServer.AccessibilityPopupType" />
|
||||
<description>
|
||||
Sets popup type for popup buttons.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_previous_on_line">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="other_id" type="RID" />
|
||||
<description>
|
||||
Sets previous element on the line.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_role">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="role" type="int" enum="DisplayServer.AccessibilityRole" />
|
||||
<description>
|
||||
Sets element accessibility role.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_role_description">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="description" type="String" />
|
||||
<description>
|
||||
Sets element accessibility role description text.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_scroll_x">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="position" type="float" />
|
||||
<description>
|
||||
Sets scroll bar x position.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_scroll_x_range">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="min" type="float" />
|
||||
<param index="2" name="max" type="float" />
|
||||
<description>
|
||||
Sets scroll bar x range.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_scroll_y">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="position" type="float" />
|
||||
<description>
|
||||
Sets scroll bar y position.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_scroll_y_range">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="min" type="float" />
|
||||
<param index="2" name="max" type="float" />
|
||||
<description>
|
||||
Sets scroll bar y range.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_shortcut">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="shortcut" type="String" />
|
||||
<description>
|
||||
Sets the list of keyboard shortcuts used by element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_state_description">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="description" type="String" />
|
||||
<description>
|
||||
Sets human-readable description of the current checked state.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_table_cell_position">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="row_index" type="int" />
|
||||
<param index="2" name="column_index" type="int" />
|
||||
<description>
|
||||
Sets cell position in the table.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_table_cell_span">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="row_span" type="int" />
|
||||
<param index="2" name="column_span" type="int" />
|
||||
<description>
|
||||
Sets cell row/column span.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_table_column_count">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="count" type="int" />
|
||||
<description>
|
||||
Sets number of columns in the table.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_table_column_index">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Sets position of the column.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_table_row_count">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="count" type="int" />
|
||||
<description>
|
||||
Sets number of rows in the table.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_table_row_index">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Sets position of the row in the table.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_text_align">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="align" type="int" enum="HorizontalAlignment" />
|
||||
<description>
|
||||
Sets element text alignment.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_text_decorations">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="underline" type="bool" />
|
||||
<param index="2" name="strikethrough" type="bool" />
|
||||
<param index="3" name="overline" type="bool" />
|
||||
<description>
|
||||
Sets text underline/overline/strikethrough.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_text_orientation">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="vertical" type="bool" />
|
||||
<description>
|
||||
Sets text orientation.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_text_selection">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="text_start_id" type="RID" />
|
||||
<param index="2" name="start_char" type="int" />
|
||||
<param index="3" name="text_end_id" type="RID" />
|
||||
<param index="4" name="end_char" type="int" />
|
||||
<description>
|
||||
Sets text selection to the text field. [param text_start_id] and [param text_end_id] should be elements created by [method accessibility_create_sub_text_edit_elements]. Character offsets are relative to the corresponding element.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_tooltip">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="tooltip" type="String" />
|
||||
<description>
|
||||
Sets tooltip text.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_transform">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="transform" type="Transform2D" />
|
||||
<description>
|
||||
Sets element 2D transform.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_url">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="url" type="String" />
|
||||
<description>
|
||||
Sets link URL.
|
||||
</description>
|
||||
</method>
|
||||
<method name="accessibility_update_set_value">
|
||||
<return type="void" />
|
||||
<param index="0" name="id" type="RID" />
|
||||
<param index="1" name="value" type="String" />
|
||||
<description>
|
||||
Sets element text value.
|
||||
</description>
|
||||
</method>
|
||||
<method name="beep" qualifiers="const">
|
||||
<return type="void" />
|
||||
<description>
|
||||
@ -1969,6 +2593,276 @@
|
||||
<constant name="FEATURE_SELF_FITTING_WINDOWS" value="33" enum="Feature">
|
||||
Display server automatically fits popups according to the screen boundaries. Window nodes should not attempt to do that themselves.
|
||||
</constant>
|
||||
<constant name="FEATURE_ACCESSIBILITY_SCREEN_READER" value="34" enum="Feature">
|
||||
Display server supports interaction with screen reader or Braille display. [b]Linux (X11/Wayland), macOS, Windows[/b]
|
||||
</constant>
|
||||
<constant name="ROLE_UNKNOWN" value="0" enum="AccessibilityRole">
|
||||
Unknown or custom role.
|
||||
</constant>
|
||||
<constant name="ROLE_DEFAULT_BUTTON" value="1" enum="AccessibilityRole">
|
||||
Default dialog button element.
|
||||
</constant>
|
||||
<constant name="ROLE_AUDIO" value="2" enum="AccessibilityRole">
|
||||
Audio player element.
|
||||
</constant>
|
||||
<constant name="ROLE_VIDEO" value="3" enum="AccessibilityRole">
|
||||
Video player element.
|
||||
</constant>
|
||||
<constant name="ROLE_STATIC_TEXT" value="4" enum="AccessibilityRole">
|
||||
Non-editable text label.
|
||||
</constant>
|
||||
<constant name="ROLE_CONTAINER" value="5" enum="AccessibilityRole">
|
||||
Container element. Elements with this role are used for internal structure and ignored by screen readers.
|
||||
</constant>
|
||||
<constant name="ROLE_PANEL" value="6" enum="AccessibilityRole">
|
||||
Panel container element.
|
||||
</constant>
|
||||
<constant name="ROLE_BUTTON" value="7" enum="AccessibilityRole">
|
||||
Button element.
|
||||
</constant>
|
||||
<constant name="ROLE_LINK" value="8" enum="AccessibilityRole">
|
||||
Link element.
|
||||
</constant>
|
||||
<constant name="ROLE_CHECK_BOX" value="9" enum="AccessibilityRole">
|
||||
Check box element.
|
||||
</constant>
|
||||
<constant name="ROLE_RADIO_BUTTON" value="10" enum="AccessibilityRole">
|
||||
Radio button element.
|
||||
</constant>
|
||||
<constant name="ROLE_CHECK_BUTTON" value="11" enum="AccessibilityRole">
|
||||
Check button element.
|
||||
</constant>
|
||||
<constant name="ROLE_SCROLL_BAR" value="12" enum="AccessibilityRole">
|
||||
Scroll bar element.
|
||||
</constant>
|
||||
<constant name="ROLE_SCROLL_VIEW" value="13" enum="AccessibilityRole">
|
||||
Scroll container element.
|
||||
</constant>
|
||||
<constant name="ROLE_SPLITTER" value="14" enum="AccessibilityRole">
|
||||
Container splitter handle element.
|
||||
</constant>
|
||||
<constant name="ROLE_SLIDER" value="15" enum="AccessibilityRole">
|
||||
Slider element.
|
||||
</constant>
|
||||
<constant name="ROLE_SPIN_BUTTON" value="16" enum="AccessibilityRole">
|
||||
Spin box element.
|
||||
</constant>
|
||||
<constant name="ROLE_PROGRESS_INDICATOR" value="17" enum="AccessibilityRole">
|
||||
Progress indicator element.
|
||||
</constant>
|
||||
<constant name="ROLE_TEXT_FIELD" value="18" enum="AccessibilityRole">
|
||||
Editable text field element.
|
||||
</constant>
|
||||
<constant name="ROLE_MULTILINE_TEXT_FIELD" value="19" enum="AccessibilityRole">
|
||||
Multiline editable text field element.
|
||||
</constant>
|
||||
<constant name="ROLE_COLOR_PICKER" value="20" enum="AccessibilityRole">
|
||||
Color picker element.
|
||||
</constant>
|
||||
<constant name="ROLE_TABLE" value="21" enum="AccessibilityRole">
|
||||
Table element.
|
||||
</constant>
|
||||
<constant name="ROLE_CELL" value="22" enum="AccessibilityRole">
|
||||
Table/tree cell element.
|
||||
</constant>
|
||||
<constant name="ROLE_ROW" value="23" enum="AccessibilityRole">
|
||||
Table/tree row element.
|
||||
</constant>
|
||||
<constant name="ROLE_ROW_GROUP" value="24" enum="AccessibilityRole">
|
||||
Table/tree row group element.
|
||||
</constant>
|
||||
<constant name="ROLE_ROW_HEADER" value="25" enum="AccessibilityRole">
|
||||
Table/tree row header element.
|
||||
</constant>
|
||||
<constant name="ROLE_COLUMN_HEADER" value="26" enum="AccessibilityRole">
|
||||
Table/tree column header element.
|
||||
</constant>
|
||||
<constant name="ROLE_TREE" value="27" enum="AccessibilityRole">
|
||||
Tree view element.
|
||||
</constant>
|
||||
<constant name="ROLE_TREE_ITEM" value="28" enum="AccessibilityRole">
|
||||
Tree view item element.
|
||||
</constant>
|
||||
<constant name="ROLE_LIST" value="29" enum="AccessibilityRole">
|
||||
List element.
|
||||
</constant>
|
||||
<constant name="ROLE_LIST_ITEM" value="30" enum="AccessibilityRole">
|
||||
List item element.
|
||||
</constant>
|
||||
<constant name="ROLE_LIST_BOX" value="31" enum="AccessibilityRole">
|
||||
List view element.
|
||||
</constant>
|
||||
<constant name="ROLE_LIST_BOX_OPTION" value="32" enum="AccessibilityRole">
|
||||
List view item element.
|
||||
</constant>
|
||||
<constant name="ROLE_TAB_BAR" value="33" enum="AccessibilityRole">
|
||||
Tab bar element.
|
||||
</constant>
|
||||
<constant name="ROLE_TAB" value="34" enum="AccessibilityRole">
|
||||
Tab bar item element.
|
||||
</constant>
|
||||
<constant name="ROLE_TAB_PANEL" value="35" enum="AccessibilityRole">
|
||||
Tab panel element.
|
||||
</constant>
|
||||
<constant name="ROLE_MENU_BAR" value="36" enum="AccessibilityRole">
|
||||
Menu bar element.
|
||||
</constant>
|
||||
<constant name="ROLE_MENU" value="37" enum="AccessibilityRole">
|
||||
Popup menu element.
|
||||
</constant>
|
||||
<constant name="ROLE_MENU_ITEM" value="38" enum="AccessibilityRole">
|
||||
Popup menu item element.
|
||||
</constant>
|
||||
<constant name="ROLE_MENU_ITEM_CHECK_BOX" value="39" enum="AccessibilityRole">
|
||||
Popup menu check button item element.
|
||||
</constant>
|
||||
<constant name="ROLE_MENU_ITEM_RADIO" value="40" enum="AccessibilityRole">
|
||||
Popup menu radio button item element.
|
||||
</constant>
|
||||
<constant name="ROLE_IMAGE" value="41" enum="AccessibilityRole">
|
||||
Image element.
|
||||
</constant>
|
||||
<constant name="ROLE_WINDOW" value="42" enum="AccessibilityRole">
|
||||
Window element.
|
||||
</constant>
|
||||
<constant name="ROLE_TITLE_BAR" value="43" enum="AccessibilityRole">
|
||||
Embedded window title bar element.
|
||||
</constant>
|
||||
<constant name="ROLE_DIALOG" value="44" enum="AccessibilityRole">
|
||||
Dialog window element.
|
||||
</constant>
|
||||
<constant name="ROLE_TOOLTIP" value="45" enum="AccessibilityRole">
|
||||
Tooltip element.
|
||||
</constant>
|
||||
<constant name="POPUP_UNKNOWN" value="0" enum="AccessibilityPopupType">
|
||||
Other/unknown popup type.
|
||||
</constant>
|
||||
<constant name="POPUP_MENU" value="1" enum="AccessibilityPopupType">
|
||||
Popup menu.
|
||||
</constant>
|
||||
<constant name="POPUP_LIST" value="2" enum="AccessibilityPopupType">
|
||||
Popup list.
|
||||
</constant>
|
||||
<constant name="POPUP_TREE" value="3" enum="AccessibilityPopupType">
|
||||
Popup tree view.
|
||||
</constant>
|
||||
<constant name="POPUP_DIALOG" value="4" enum="AccessibilityPopupType">
|
||||
Popup dialog.
|
||||
</constant>
|
||||
<constant name="FLAG_HIDDEN" value="0" enum="AccessibilityFlags">
|
||||
Element is hidden for accessibility tools.
|
||||
</constant>
|
||||
<constant name="FLAG_LINKED" value="1" enum="AccessibilityFlags">
|
||||
</constant>
|
||||
<constant name="FLAG_MULTISELECTABLE" value="2" enum="AccessibilityFlags">
|
||||
Element is support multiple item selection.
|
||||
</constant>
|
||||
<constant name="FLAG_REQUIRED" value="3" enum="AccessibilityFlags">
|
||||
Element require user input.
|
||||
</constant>
|
||||
<constant name="FLAG_VISITED" value="4" enum="AccessibilityFlags">
|
||||
Element is a visited link.
|
||||
</constant>
|
||||
<constant name="FLAG_BUSY" value="5" enum="AccessibilityFlags">
|
||||
Element content is not ready (e.g. loading).
|
||||
</constant>
|
||||
<constant name="FLAG_MODAL" value="6" enum="AccessibilityFlags">
|
||||
Element is modal window.
|
||||
</constant>
|
||||
<constant name="FLAG_TOUCH_PASSTHROUGH" value="7" enum="AccessibilityFlags">
|
||||
Element allows touches to be passed through when a screen reader is in touch exploration mode.
|
||||
</constant>
|
||||
<constant name="FLAG_READONLY" value="8" enum="AccessibilityFlags">
|
||||
Element is text field with selectable but read-only text.
|
||||
</constant>
|
||||
<constant name="FLAG_DISABLED" value="9" enum="AccessibilityFlags">
|
||||
Element is disabled.
|
||||
</constant>
|
||||
<constant name="FLAG_CLIPS_CHILDREN" value="10" enum="AccessibilityFlags">
|
||||
Element clips children.
|
||||
</constant>
|
||||
<constant name="ACTION_CLICK" value="0" enum="AccessibilityAction">
|
||||
Single click action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_FOCUS" value="1" enum="AccessibilityAction">
|
||||
Focus action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_BLUR" value="2" enum="AccessibilityAction">
|
||||
Blur action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_COLLAPSE" value="3" enum="AccessibilityAction">
|
||||
Collapse action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_EXPAND" value="4" enum="AccessibilityAction">
|
||||
Expand action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_DECREMENT" value="5" enum="AccessibilityAction">
|
||||
Decrement action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_INCREMENT" value="6" enum="AccessibilityAction">
|
||||
Increment action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_HIDE_TOOLTIP" value="7" enum="AccessibilityAction">
|
||||
Hide tooltip action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_SHOW_TOOLTIP" value="8" enum="AccessibilityAction">
|
||||
Show tooltip action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_SET_TEXT_SELECTION" value="9" enum="AccessibilityAction">
|
||||
Set text selection action, callback argument is set to [Dictionary] with the following keys:
|
||||
- [code]"start_element"[/code] accessibility element of the selection start.
|
||||
- [code]"start_char"[/code] character offset relative to the accessibility element of the selection start.
|
||||
- [code]"end_element"[/code] accessibility element of the selection end.
|
||||
- [code]"end_char"[/code] character offset relative to the accessibility element of the selection end.
|
||||
</constant>
|
||||
<constant name="ACTION_REPLACE_SELECTED_TEXT" value="10" enum="AccessibilityAction">
|
||||
Replace text action, callback argument is set to [String] with the replacement text.
|
||||
</constant>
|
||||
<constant name="ACTION_SCROLL_BACKWARD" value="11" enum="AccessibilityAction">
|
||||
Scroll backward action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_SCROLL_DOWN" value="12" enum="AccessibilityAction">
|
||||
Scroll down action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_SCROLL_FORWARD" value="13" enum="AccessibilityAction">
|
||||
Scroll forward action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_SCROLL_LEFT" value="14" enum="AccessibilityAction">
|
||||
Scroll left action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_SCROLL_RIGHT" value="15" enum="AccessibilityAction">
|
||||
Scroll right action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_SCROLL_UP" value="16" enum="AccessibilityAction">
|
||||
Scroll up action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_SCROLL_INTO_VIEW" value="17" enum="AccessibilityAction">
|
||||
Scroll into view action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_SCROLL_TO_POINT" value="18" enum="AccessibilityAction">
|
||||
Scroll to point action, callback argument is set to [Vector2] with the relative point coordinates.
|
||||
</constant>
|
||||
<constant name="ACTION_SET_SCROLL_OFFSET" value="19" enum="AccessibilityAction">
|
||||
Set scroll offset action, callback argument is set to [Vector2] with the scroll offset.
|
||||
</constant>
|
||||
<constant name="ACTION_SET_VALUE" value="20" enum="AccessibilityAction">
|
||||
Set value action action, callback argument is set to [String] or number with the new value.
|
||||
</constant>
|
||||
<constant name="ACTION_SHOW_CONTEXT_MENU" value="21" enum="AccessibilityAction">
|
||||
Show context menu action, callback argument is not set.
|
||||
</constant>
|
||||
<constant name="ACTION_CUSTOM" value="22" enum="AccessibilityAction">
|
||||
Custom action, callback argument is set to the integer action id.
|
||||
</constant>
|
||||
<constant name="LIVE_OFF" value="0" enum="AccessibilityLiveMode">
|
||||
Indicates that updates to the live region should not be presented.
|
||||
</constant>
|
||||
<constant name="LIVE_POLITE" value="1" enum="AccessibilityLiveMode">
|
||||
Indicates that updates to the live region should be presented at the next opportunity (for example at the end of speaking the current sentence).
|
||||
</constant>
|
||||
<constant name="LIVE_ASSERTIVE" value="2" enum="AccessibilityLiveMode">
|
||||
Indicates that updates to the live region have the highest priority and should be presented immediately.
|
||||
</constant>
|
||||
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
|
||||
Makes the mouse cursor visible if it is hidden.
|
||||
</constant>
|
||||
|
@ -391,6 +391,9 @@
|
||||
<member name="snapping_enabled" type="bool" setter="set_snapping_enabled" getter="is_snapping_enabled" default="true">
|
||||
If [code]true[/code], enables snapping.
|
||||
</member>
|
||||
<member name="type_names" type="Dictionary" setter="set_type_names" getter="get_type_names" default="{}">
|
||||
[Dictionary] of human readable port type names.
|
||||
</member>
|
||||
<member name="zoom" type="float" setter="set_zoom" getter="get_zoom" default="1.0">
|
||||
The current zoom value.
|
||||
</member>
|
||||
@ -603,5 +606,8 @@
|
||||
<theme_item name="panel" data_type="style" type="StyleBox">
|
||||
The background drawn under the grid.
|
||||
</theme_item>
|
||||
<theme_item name="panel_focus" data_type="style" type="StyleBox">
|
||||
[StyleBox] used when the [GraphEdit] is focused (when used with assistive apps).
|
||||
</theme_item>
|
||||
</theme_items>
|
||||
</class>
|
||||
|
@ -267,6 +267,7 @@
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="3" />
|
||||
<member name="ignore_invalid_connection_type" type="bool" setter="set_ignore_invalid_connection_type" getter="is_ignoring_valid_connection_type" default="false">
|
||||
If [code]true[/code], you can connect ports with different types, even if the connection was not explicitly allowed in the parent [GraphEdit].
|
||||
</member>
|
||||
@ -299,12 +300,18 @@
|
||||
<theme_item name="panel" data_type="style" type="StyleBox">
|
||||
The default background for the slot area of the [GraphNode].
|
||||
</theme_item>
|
||||
<theme_item name="panel_focus" data_type="style" type="StyleBox">
|
||||
[StyleBox] used when the [GraphNode] is focused (when used with assistive apps).
|
||||
</theme_item>
|
||||
<theme_item name="panel_selected" data_type="style" type="StyleBox">
|
||||
The [StyleBox] used for the slot area when selected.
|
||||
</theme_item>
|
||||
<theme_item name="slot" data_type="style" type="StyleBox">
|
||||
The [StyleBox] used for each slot of the [GraphNode].
|
||||
</theme_item>
|
||||
<theme_item name="slot_selected" data_type="style" type="StyleBox">
|
||||
[StyleBox] used when the slot is focused (when used with assistive apps).
|
||||
</theme_item>
|
||||
<theme_item name="titlebar" data_type="style" type="StyleBox">
|
||||
The [StyleBox] used for the title bar of the [GraphNode].
|
||||
</theme_item>
|
||||
|
@ -90,6 +90,13 @@
|
||||
If [param exact_match] is [code]false[/code], it ignores additional input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_action_description" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="action" type="StringName" />
|
||||
<description>
|
||||
Returns the human-readable description of the given action.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_actions">
|
||||
<return type="StringName[]" />
|
||||
<description>
|
||||
|
@ -58,6 +58,7 @@
|
||||
<member name="ellipsis_char" type="String" setter="set_ellipsis_char" getter="get_ellipsis_char" default=""…"">
|
||||
Ellipsis character used for text clipping.
|
||||
</member>
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="3" />
|
||||
<member name="horizontal_alignment" type="int" setter="set_horizontal_alignment" getter="get_horizontal_alignment" enum="HorizontalAlignment" default="0">
|
||||
Controls the text's horizontal alignment. Supports left, center, right, and fill, or justify. Set it to one of the [enum HorizontalAlignment] constants.
|
||||
</member>
|
||||
@ -153,6 +154,9 @@
|
||||
<theme_item name="font_size" data_type="font_size" type="int">
|
||||
Font size of the [Label]'s text.
|
||||
</theme_item>
|
||||
<theme_item name="focus" data_type="style" type="StyleBox">
|
||||
[StyleBox] used when the [Label] is focused (when used with assistive apps).
|
||||
</theme_item>
|
||||
<theme_item name="normal" data_type="style" type="StyleBox">
|
||||
Background [StyleBox] for the [Label].
|
||||
</theme_item>
|
||||
|
@ -10,7 +10,7 @@
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="0" />
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="3" />
|
||||
<member name="language" type="String" setter="set_language" getter="get_language" default="""">
|
||||
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
|
||||
</member>
|
||||
|
@ -100,6 +100,7 @@
|
||||
<member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false">
|
||||
Flat [MenuBar] don't display item decoration.
|
||||
</member>
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="2" />
|
||||
<member name="language" type="String" setter="set_language" getter="get_language" default="""">
|
||||
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
|
||||
</member>
|
||||
|
@ -34,7 +34,7 @@
|
||||
<members>
|
||||
<member name="action_mode" type="int" setter="set_action_mode" getter="get_action_mode" overrides="BaseButton" enum="BaseButton.ActionMode" default="0" />
|
||||
<member name="flat" type="bool" setter="set_flat" getter="is_flat" overrides="Button" default="true" />
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="0" />
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="3" />
|
||||
<member name="item_count" type="int" setter="set_item_count" getter="get_item_count" default="0">
|
||||
The number of items currently in the list.
|
||||
</member>
|
||||
|
@ -36,6 +36,20 @@
|
||||
Corresponds to the [constant NOTIFICATION_EXIT_TREE] notification in [method Object._notification] and signal [signal tree_exiting]. To get notified when the node has already left the active tree, connect to the [signal tree_exited].
|
||||
</description>
|
||||
</method>
|
||||
<method name="_get_accessibility_configuration_warnings" qualifiers="virtual const">
|
||||
<return type="PackedStringArray" />
|
||||
<description>
|
||||
The elements in the array returned from this method are displayed as warnings in the Scene dock if the script that overrides it is a [code]tool[/code] script, and accessibility warnings are enabled in the editor settings.
|
||||
Returning an empty array produces no warnings.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_get_accessibility_container_name" qualifiers="virtual const">
|
||||
<return type="String" />
|
||||
<param index="0" name="node" type="Node" />
|
||||
<description>
|
||||
Return a human-readable description of the position of [param node] child in the custom container, added to the node name.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_get_configuration_warnings" qualifiers="virtual const">
|
||||
<return type="PackedStringArray" />
|
||||
<description>
|
||||
@ -56,6 +70,12 @@
|
||||
[/codeblock]
|
||||
</description>
|
||||
</method>
|
||||
<method name="_get_focused_accessibility_element" qualifiers="virtual const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Called during accessibility information updates to determine the currently focused sub-element, should return a sub-element RID or the value returned by [method get_accessibility_element].
|
||||
</description>
|
||||
</method>
|
||||
<method name="_input" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="event" type="InputEvent" />
|
||||
@ -299,6 +319,13 @@
|
||||
[b]Note:[/b] As this method walks upwards in the scene tree, it can be slow in large, deeply nested nodes. Consider storing a reference to the found node in a variable. Alternatively, use [method get_node] with unique names (see [member unique_name_in_owner]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_accessibility_element" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
Returns main accessibility element RID.
|
||||
[b]Note:[/b] This method should be called only during accessibility information updates ([constant NOTIFICATION_ACCESSIBILITY_UPDATE]).
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_child" qualifiers="const">
|
||||
<return type="Node" />
|
||||
<param index="0" name="idx" type="int" />
|
||||
@ -777,6 +804,12 @@
|
||||
Calls [method Object.notification] with [param what] on this node and all of its children, recursively.
|
||||
</description>
|
||||
</method>
|
||||
<method name="queue_accessibility_update">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Queues an accessibility information update for this node.
|
||||
</description>
|
||||
</method>
|
||||
<method name="queue_free" keywords="delete, remove, kill, die">
|
||||
<return type="void" />
|
||||
<description>
|
||||
@ -994,6 +1027,27 @@
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="accessibility_controls_nodes" type="NodePath[]" setter="set_accessibility_controls_nodes" getter="get_accessibility_controls_nodes" default="[]">
|
||||
The list of nodes which are controlled by this node.
|
||||
</member>
|
||||
<member name="accessibility_described_by_nodes" type="NodePath[]" setter="set_accessibility_described_by_nodes" getter="get_accessibility_described_by_nodes" default="[]">
|
||||
The list of nodes which are describing this node.
|
||||
</member>
|
||||
<member name="accessibility_description" type="String" setter="set_accessibility_description" getter="get_accessibility_description" default="""">
|
||||
The human-readable node description that is reported to assistive apps.
|
||||
</member>
|
||||
<member name="accessibility_flow_to_nodes" type="NodePath[]" setter="set_accessibility_flow_to_nodes" getter="get_accessibility_flow_to_nodes" default="[]">
|
||||
The list of nodes which this node flows into.
|
||||
</member>
|
||||
<member name="accessibility_labeled_by_nodes" type="NodePath[]" setter="set_accessibility_labeled_by_nodes" getter="get_accessibility_labeled_by_nodes" default="[]">
|
||||
The list of nodes which label this node.
|
||||
</member>
|
||||
<member name="accessibility_live" type="int" setter="set_accessibility_live" getter="get_accessibility_live" enum="DisplayServer.AccessibilityLiveMode" default="0">
|
||||
Live region update mode, a live region is [Node] that is updated as a result of an external event when user focus may be elsewhere.
|
||||
</member>
|
||||
<member name="accessibility_name" type="String" setter="set_accessibility_name" getter="get_accessibility_name" default="""">
|
||||
The human-readable node name that is reported to assistive apps.
|
||||
</member>
|
||||
<member name="auto_translate_mode" type="int" setter="set_auto_translate_mode" getter="get_auto_translate_mode" enum="Node.AutoTranslateMode" default="0">
|
||||
Defines if any text should automatically change to its translated version depending on the current locale (for nodes such as [Label], [RichTextLabel], [Window], etc.). Also decides if the node's strings should be parsed for POT generation.
|
||||
[b]Note:[/b] For the root node, auto translate mode can also be set via [member ProjectSettings.internationalization/rendering/root_node_auto_translate].
|
||||
@ -1276,6 +1330,12 @@
|
||||
<constant name="NOTIFICATION_TEXT_SERVER_CHANGED" value="2018">
|
||||
Notification received when the [TextServer] is changed.
|
||||
</constant>
|
||||
<constant name="NOTIFICATION_ACCESSIBILITY_UPDATE" value="3000">
|
||||
Notification received when an accessibility information update is required.
|
||||
</constant>
|
||||
<constant name="NOTIFICATION_ACCESSIBILITY_INVALIDATE" value="3001">
|
||||
Notification received when accessibility elements are invalidated. All node accessibility elements are automatically deleted after receiving this message, therefore all existing references to such elements should be discarded.
|
||||
</constant>
|
||||
<constant name="PROCESS_MODE_INHERIT" value="0" enum="ProcessMode">
|
||||
Inherits [member process_mode] from the node's parent. This is the default for any newly created node.
|
||||
</constant>
|
||||
|
@ -235,6 +235,16 @@
|
||||
</method>
|
||||
</methods>
|
||||
<members>
|
||||
<member name="accessibility/general/accessibility_support" type="int" setter="" getter="" default="0">
|
||||
Accessibility support mode:
|
||||
- [b]Auto[/b] ([code]0[/code]): accessibility support is enabled, but accessibility information updates are processed only if an assistive app (e.g. screen reader or Braille display) is active (default).
|
||||
- [b]Always Active[/b] ([code]1[/code]): accessibility support is enabled, and accessibility information updates are processed regardless of current assistive apps' status.
|
||||
- [b]Disabled[/b] ([code]2[/code]): accessibility support is fully disabled.
|
||||
[b]Note:[/b] Accessibility debugging tools, such as Accessibility Insights for Windows, macOS Accessibility Inspector, or AT-SPI Browser do not count as assistive apps. To test your app with these tools, use [code]1[/code].
|
||||
</member>
|
||||
<member name="accessibility/general/updates_per_second" type="int" setter="" getter="" default="60">
|
||||
The number of accessibility information updates per second.
|
||||
</member>
|
||||
<member name="animation/warnings/check_angle_interpolation_type_conflicting" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], [AnimationMixer] prints the warning of interpolation being forced to choose the shortest rotation path due to multiple angle interpolation types being mixed in the [AnimationMixer] cache.
|
||||
</member>
|
||||
@ -1191,6 +1201,10 @@
|
||||
Default [InputEventAction] to confirm a focused button, menu or list item, or validate input.
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
</member>
|
||||
<member name="input/ui_accessibility_drag_and_drop" type="Dictionary" setter="" getter="">
|
||||
Default [InputEventAction] to start or end a drag-and-drop operation without using mouse.
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
</member>
|
||||
<member name="input/ui_cancel" type="Dictionary" setter="" getter="">
|
||||
Default [InputEventAction] to discard a modal or pending input.
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
@ -1227,6 +1241,10 @@
|
||||
Default [InputEventAction] to go up one directory in a [FileDialog].
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
</member>
|
||||
<member name="input/ui_focus_mode" type="Dictionary" setter="" getter="">
|
||||
Default [InputEventAction] to switch [TextEdit] [member input/ui_text_indent] between moving keyboard focus to the next [Control] in the scene and inputting a [code]Tab[/code] character.
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
</member>
|
||||
<member name="input/ui_focus_next" type="Dictionary" setter="" getter="">
|
||||
Default [InputEventAction] to focus the next [Control] in the scene. The focus behavior can be configured via [member Control.focus_next].
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
@ -1243,6 +1261,20 @@
|
||||
Default [InputEventAction] to duplicate a [GraphNode] in a [GraphEdit].
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
</member>
|
||||
<member name="input/ui_graph_follow_left" type="Dictionary" setter="" getter="">
|
||||
Default [InputEventAction] to follow a [GraphNode] input port connection.
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
</member>
|
||||
<member name="input/ui_graph_follow_left.macos" type="Dictionary" setter="" getter="">
|
||||
macOS specific override for the shortcut to follow a [GraphNode] input port connection.
|
||||
</member>
|
||||
<member name="input/ui_graph_follow_right" type="Dictionary" setter="" getter="">
|
||||
Default [InputEventAction] to follow a [GraphNode] output port connection.
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
</member>
|
||||
<member name="input/ui_graph_follow_right.macos" type="Dictionary" setter="" getter="">
|
||||
macOS specific override for the shortcut to follow a [GraphNode] output port connection.
|
||||
</member>
|
||||
<member name="input/ui_home" type="Dictionary" setter="" getter="">
|
||||
Default [InputEventAction] to go to the start position of a [Control] (e.g. first item in an [ItemList] or a [Tree]), matching the behavior of [constant KEY_HOME] on typical desktop UI systems.
|
||||
[b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified.
|
||||
|
@ -29,6 +29,7 @@
|
||||
<param index="7" name="pad" type="bool" default="false" />
|
||||
<param index="8" name="tooltip" type="String" default="""" />
|
||||
<param index="9" name="size_in_percent" type="bool" default="false" />
|
||||
<param index="10" name="alt_text" type="String" default="""" />
|
||||
<description>
|
||||
Adds an image's opening and closing tags to the tag stack, optionally providing a [param width] and [param height] to resize the image, a [param color] to tint the image and a [param region] to only use parts of the image.
|
||||
If [param width] or [param height] is set to 0, the image size will be adjusted in order to keep the original aspect ratio.
|
||||
@ -36,6 +37,7 @@
|
||||
[param key] is an optional identifier, that can be used to modify the image via [method update_image].
|
||||
If [param pad] is set, and the image is smaller than the size specified by [param width] and [param height], the image padding is added to match the size instead of upscaling.
|
||||
If [param size_in_percent] is set, [param width] and [param height] values are percentages of the control width instead of pixels.
|
||||
[param alt_text] is used as the image description for assistive apps.
|
||||
</description>
|
||||
</method>
|
||||
<method name="add_text">
|
||||
@ -517,8 +519,9 @@
|
||||
<param index="0" name="columns" type="int" />
|
||||
<param index="1" name="inline_align" type="int" enum="InlineAlignment" default="0" />
|
||||
<param index="2" name="align_to_row" type="int" default="-1" />
|
||||
<param index="3" name="name" type="String" default="""" />
|
||||
<description>
|
||||
Adds a [code skip-lint][table=columns,inline_align][/code] tag to the tag stack. Use [method set_table_column_expand] to set column expansion ratio. Use [method push_cell] to add cells.
|
||||
Adds a [code skip-lint][table=columns,inline_align][/code] tag to the tag stack. Use [method set_table_column_expand] to set column expansion ratio. Use [method push_cell] to add cells. [param name] is used as the table name for assistive apps.
|
||||
</description>
|
||||
</method>
|
||||
<method name="push_underline">
|
||||
@ -612,6 +615,14 @@
|
||||
If [param expand] is [code]false[/code], the column will not contribute to the total ratio.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_table_column_name">
|
||||
<return type="void" />
|
||||
<param index="0" name="column" type="int" />
|
||||
<param index="1" name="name" type="String" />
|
||||
<description>
|
||||
Sets table column name for assistive apps.
|
||||
</description>
|
||||
</method>
|
||||
<method name="update_image">
|
||||
<return type="void" />
|
||||
<param index="0" name="key" type="Variant" />
|
||||
@ -658,6 +669,7 @@
|
||||
<member name="fit_content" type="bool" setter="set_fit_content" getter="is_fit_content_enabled" default="false">
|
||||
If [code]true[/code], the label's minimum size will be automatically updated to fit its content, matching the behavior of [Label].
|
||||
</member>
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="3" />
|
||||
<member name="hint_underlined" type="bool" setter="set_hint_underline" getter="is_hint_underlined" default="true">
|
||||
If [code]true[/code], the label underlines hint tags such as [code skip-lint][hint=description]{text}[/hint][/code].
|
||||
</member>
|
||||
|
@ -152,6 +152,18 @@
|
||||
Returns [code]true[/code] if a node added to the given group [param name] exists in the tree.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_accessibility_enabled" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if accessibility features are enabled, and accessibility information updates are actively processed.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_accessibility_supported" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
Returns [code]true[/code] if accessibility features are supported by the OS and enabled in project settings.
|
||||
</description>
|
||||
</method>
|
||||
<method name="notify_group">
|
||||
<return type="void" />
|
||||
<param index="0" name="group" type="StringName" />
|
||||
|
@ -12,6 +12,7 @@
|
||||
<member name="custom_step" type="float" setter="set_custom_step" getter="get_custom_step" default="-1.0">
|
||||
Overrides the step used when clicking increment and decrement buttons or when using arrow keys when the [ScrollBar] is focused.
|
||||
</member>
|
||||
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="3" />
|
||||
<member name="step" type="float" setter="set_step" getter="get_step" overrides="Range" default="0.0" />
|
||||
</members>
|
||||
<signals>
|
||||
|
@ -1380,6 +1380,9 @@
|
||||
The syntax highlighter to use.
|
||||
[b]Note:[/b] A [SyntaxHighlighter] instance should not be used across multiple [TextEdit] nodes.
|
||||
</member>
|
||||
<member name="tab_input_mode" type="bool" setter="set_tab_input_mode" getter="get_tab_input_mode" default="true">
|
||||
If [code]true[/code], [member ProjectSettings.input/ui_text_indent] input [code]Tab[/code] character, otherwise it moves keyboard focus to the next [Control] in the scene.
|
||||
</member>
|
||||
<member name="text" type="String" setter="set_text" getter="get_text" default="""">
|
||||
String value of the [TextEdit].
|
||||
</member>
|
||||
|
@ -56,6 +56,12 @@
|
||||
Draw text into a canvas item at a given position, with [param color]. [param pos] specifies the top left corner of the bounding box.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_inferred_direction" qualifiers="const">
|
||||
<return type="int" enum="TextServer.Direction" />
|
||||
<description>
|
||||
Returns the text writing direction inferred by the BiDi algorithm.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_line_ascent" qualifiers="const">
|
||||
<return type="float" />
|
||||
<description>
|
||||
|
@ -122,6 +122,12 @@
|
||||
Returns drop cap bounding box size.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_inferred_direction" qualifiers="const">
|
||||
<return type="int" enum="TextServer.Direction" />
|
||||
<description>
|
||||
Returns the text writing direction inferred by the BiDi algorithm.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_line_ascent" qualifiers="const">
|
||||
<return type="float" />
|
||||
<param index="0" name="line" type="int" />
|
||||
@ -205,6 +211,12 @@
|
||||
Returns the size of the bounding box of the paragraph, without line breaks.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_range" qualifiers="const">
|
||||
<return type="Vector2i" />
|
||||
<description>
|
||||
Returns the character range of the paragraph.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_rid" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<description>
|
||||
|
@ -1215,6 +1215,69 @@
|
||||
[b]Note:[/b] This function is used by during project export, to include TextServer database.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_run_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<description>
|
||||
Returns the number of uniform text runs in the buffer.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_run_direction" qualifiers="const">
|
||||
<return type="int" enum="TextServer.Direction" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the direction of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_run_font_rid" qualifiers="const">
|
||||
<return type="RID" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the font RID of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_run_font_size" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the font size of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_run_language" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the language of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_run_object" qualifiers="const">
|
||||
<return type="Variant" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the embedded object of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_run_range" qualifiers="const">
|
||||
<return type="Vector2i" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the source text range of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_run_text" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the source text of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_span_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
@ -1238,6 +1301,29 @@
|
||||
Returns text span metadata.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_span_object" qualifiers="const">
|
||||
<return type="Variant" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the text span embedded object key.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_span_text" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
Returns the text span source text.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_get_text" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<description>
|
||||
Returns the text buffer source text, including object replacement characters.
|
||||
</description>
|
||||
</method>
|
||||
<method name="shaped_set_span_update_font">
|
||||
<return type="void" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
|
@ -1323,6 +1323,77 @@
|
||||
Saves optional TextServer database (e.g. ICU break iterators and dictionaries) to the file.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_run_count" qualifiers="virtual const">
|
||||
<return type="int" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the number of uniform text runs in the buffer.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_run_direction" qualifiers="virtual const">
|
||||
<return type="int" enum="TextServer.Direction" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the direction of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_run_font_rid" qualifiers="virtual const">
|
||||
<return type="RID" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the font RID of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_run_font_size" qualifiers="virtual const">
|
||||
<return type="int" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the font size of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_run_language" qualifiers="virtual const">
|
||||
<return type="String" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the language of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_run_object" qualifiers="virtual const">
|
||||
<return type="Variant" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the embedded object of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_run_range" qualifiers="virtual const">
|
||||
<return type="Vector2i" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the source text range of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_run_text" qualifiers="virtual const">
|
||||
<return type="String" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the source text of the [param index] text run (in visual order).
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_span_count" qualifiers="virtual const">
|
||||
<return type="int" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
@ -1349,6 +1420,32 @@
|
||||
Returns text span metadata.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_span_object" qualifiers="virtual const">
|
||||
<return type="Variant" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the text span embedded object key.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_span_text" qualifiers="virtual const">
|
||||
<return type="String" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<param index="1" name="index" type="int" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the text span source text.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_get_text" qualifiers="virtual const">
|
||||
<return type="String" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
<description>
|
||||
[b]Required.[/b]
|
||||
Returns the text buffer source text, including object replacement characters.
|
||||
</description>
|
||||
</method>
|
||||
<method name="_shaped_set_span_update_font" qualifiers="virtual">
|
||||
<return type="void" />
|
||||
<param index="0" name="shaped" type="RID" />
|
||||
|
@ -18,8 +18,9 @@
|
||||
<param index="2" name="id" type="int" default="-1" />
|
||||
<param index="3" name="disabled" type="bool" default="false" />
|
||||
<param index="4" name="tooltip_text" type="String" default="""" />
|
||||
<param index="5" name="alt_text" type="String" default="""" />
|
||||
<description>
|
||||
Adds a button with [Texture2D] [param button] to the end of the cell at column [param column]. The [param id] is used to identify the button in the according [signal Tree.button_clicked] signal and can be different from the buttons index. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately before this method. Optionally, the button can be [param disabled] and have a [param tooltip_text].
|
||||
Adds a button with [Texture2D] [param button] to the end of the cell at column [param column]. The [param id] is used to identify the button in the according [signal Tree.button_clicked] signal and can be different from the buttons index. If not specified, the next available index is used, which may be retrieved by calling [method get_button_count] immediately before this method. Optionally, the button can be [param disabled] and have a [param tooltip_text]. [param alt_text] is used as the button description for assistive apps.
|
||||
</description>
|
||||
</method>
|
||||
<method name="add_child">
|
||||
@ -79,6 +80,13 @@
|
||||
Removes the button at index [param button_index] in column [param column].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_alt_text" qualifiers="const">
|
||||
<return type="String" />
|
||||
<param index="0" name="column" type="int" />
|
||||
<description>
|
||||
Returns the given column's alternative text.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_auto_translate_mode" qualifiers="const">
|
||||
<return type="int" enum="Node.AutoTranslateMode" />
|
||||
<param index="0" name="column" type="int" />
|
||||
@ -506,6 +514,14 @@
|
||||
Selects the given [param column].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_alt_text">
|
||||
<return type="void" />
|
||||
<param index="0" name="column" type="int" />
|
||||
<param index="1" name="text" type="String" />
|
||||
<description>
|
||||
Sets the given column's alternative (description) text for assistive apps.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_auto_translate_mode">
|
||||
<return type="void" />
|
||||
<param index="0" name="column" type="int" />
|
||||
@ -532,6 +548,15 @@
|
||||
Sets the given column's button [Texture2D] at index [param button_index] to [param button].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_button_alt_text">
|
||||
<return type="void" />
|
||||
<param index="0" name="column" type="int" />
|
||||
<param index="1" name="button_index" type="int" />
|
||||
<param index="2" name="alt_text" type="String" />
|
||||
<description>
|
||||
Sets the given column's button alternative text (description) at index [param button_index] for assistive apps.
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_button_color">
|
||||
<return type="void" />
|
||||
<param index="0" name="column" type="int" />
|
||||
|
@ -148,6 +148,12 @@
|
||||
Returns the drag data from the GUI, that was previously returned by [method Control._get_drag_data].
|
||||
</description>
|
||||
</method>
|
||||
<method name="gui_get_drag_description" qualifiers="const">
|
||||
<return type="String" />
|
||||
<description>
|
||||
Returns the drag data human-readable description.
|
||||
</description>
|
||||
</method>
|
||||
<method name="gui_get_focus_owner" qualifiers="const">
|
||||
<return type="Control" />
|
||||
<description>
|
||||
@ -180,6 +186,13 @@
|
||||
Removes the focus from the currently focused [Control] within this viewport. If no [Control] has the focus, does nothing.
|
||||
</description>
|
||||
</method>
|
||||
<method name="gui_set_drag_description">
|
||||
<return type="void" />
|
||||
<param index="0" name="description" type="String" />
|
||||
<description>
|
||||
Sets the drag data human-readable description.
|
||||
</description>
|
||||
</method>
|
||||
<method name="is_input_handled" qualifiers="const">
|
||||
<return type="bool" />
|
||||
<description>
|
||||
|
@ -108,6 +108,12 @@
|
||||
Returns [code]true[/code] if the [param flag] is set.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_focused_window" qualifiers="static">
|
||||
<return type="Window" />
|
||||
<description>
|
||||
Returns the focused window.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_layout_direction" qualifiers="const">
|
||||
<return type="int" enum="Window.LayoutDirection" />
|
||||
<description>
|
||||
|
@ -202,6 +202,8 @@ static int audio_driver_idx = -1;
|
||||
|
||||
// Engine config/tools
|
||||
|
||||
static DisplayServer::AccessibilityMode accessibility_mode = DisplayServer::AccessibilityMode::ACCESSIBILITY_AUTO;
|
||||
static bool accessibility_mode_set = false;
|
||||
static bool single_window = false;
|
||||
static bool editor = false;
|
||||
static bool project_manager = false;
|
||||
@ -616,6 +618,7 @@ void Main::print_help(const char *p_binary) {
|
||||
print_help_option("--xr-mode <mode>", "Select XR (Extended Reality) mode [\"default\", \"off\", \"on\"].\n");
|
||||
#endif
|
||||
print_help_option("--wid <window_id>", "Request parented to window.\n");
|
||||
print_help_option("--accessibility <mode>", "Select accessibility mode ['auto' (when screen reader is running, default), 'always', 'disabled'].\n");
|
||||
|
||||
print_help_title("Debug options");
|
||||
print_help_option("-d, --debug", "Debug (local stdout debugger).\n");
|
||||
@ -1300,6 +1303,27 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
} else if (arg == "--single-window") { // force single window
|
||||
|
||||
single_window = true;
|
||||
} else if (arg == "--accessibility") {
|
||||
if (N) {
|
||||
String string = N->get();
|
||||
if (string == "auto") {
|
||||
accessibility_mode = DisplayServer::AccessibilityMode::ACCESSIBILITY_AUTO;
|
||||
accessibility_mode_set = true;
|
||||
} else if (string == "always") {
|
||||
accessibility_mode = DisplayServer::AccessibilityMode::ACCESSIBILITY_ALWAYS;
|
||||
accessibility_mode_set = true;
|
||||
} else if (string == "disabled") {
|
||||
accessibility_mode = DisplayServer::AccessibilityMode::ACCESSIBILITY_DISABLED;
|
||||
accessibility_mode_set = true;
|
||||
} else {
|
||||
OS::get_singleton()->print("Accessibility mode argument not recognized, aborting.\n");
|
||||
goto error;
|
||||
}
|
||||
N = N->next();
|
||||
} else {
|
||||
OS::get_singleton()->print("Missing accessibility mode argument, aborting.\n");
|
||||
goto error;
|
||||
}
|
||||
} else if (arg == "-t" || arg == "--always-on-top") { // force always-on-top window
|
||||
|
||||
init_always_on_top = true;
|
||||
@ -2360,14 +2384,16 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
default_renderer_mobile = "gl_compatibility";
|
||||
}
|
||||
#endif
|
||||
if (renderer_hints.is_empty()) {
|
||||
ERR_PRINT("No renderers available.");
|
||||
if (!renderer_hints.is_empty()) {
|
||||
renderer_hints += ",";
|
||||
}
|
||||
renderer_hints += "dummy";
|
||||
|
||||
if (!rendering_method.is_empty()) {
|
||||
if (rendering_method != "forward_plus" &&
|
||||
rendering_method != "mobile" &&
|
||||
rendering_method != "gl_compatibility") {
|
||||
rendering_method != "gl_compatibility" &&
|
||||
rendering_method != "dummy") {
|
||||
OS::get_singleton()->print("Unknown rendering method '%s', aborting.\nValid options are ",
|
||||
rendering_method.utf8().get_data());
|
||||
|
||||
@ -2435,7 +2461,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
|
||||
// Set a default renderer if none selected. Try to choose one that matches the driver.
|
||||
if (rendering_method.is_empty()) {
|
||||
if (rendering_driver == "opengl3" || rendering_driver == "opengl3_angle" || rendering_driver == "opengl3_es") {
|
||||
if (rendering_driver == "dummy") {
|
||||
rendering_method = "dummy";
|
||||
} else if (rendering_driver == "opengl3" || rendering_driver == "opengl3_angle" || rendering_driver == "opengl3_es") {
|
||||
rendering_method = "gl_compatibility";
|
||||
} else {
|
||||
rendering_method = "forward_plus";
|
||||
@ -2463,6 +2491,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
available_drivers.push_back("opengl3_es");
|
||||
}
|
||||
#endif
|
||||
if (rendering_method == "dummy") {
|
||||
available_drivers.push_back("dummy");
|
||||
}
|
||||
if (available_drivers.is_empty()) {
|
||||
OS::get_singleton()->print("Unknown renderer name '%s', aborting.\n", rendering_method.utf8().get_data());
|
||||
goto error;
|
||||
@ -2499,7 +2530,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||
}
|
||||
|
||||
if (rendering_driver.is_empty()) {
|
||||
if (rendering_method == "gl_compatibility") {
|
||||
if (rendering_method == "dummy") {
|
||||
rendering_driver = "dummy";
|
||||
} else if (rendering_method == "gl_compatibility") {
|
||||
rendering_driver = GLOBAL_GET("rendering/gl_compatibility/driver");
|
||||
} else {
|
||||
rendering_driver = GLOBAL_GET("rendering/rendering_device/driver");
|
||||
@ -3095,6 +3128,11 @@ Error Main::setup2(bool p_show_boot_logo) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!accessibility_mode_set) {
|
||||
accessibility_mode = (DisplayServer::AccessibilityMode)GLOBAL_GET("accessibility/general/accessibility_support").operator int64_t();
|
||||
}
|
||||
DisplayServer::accessibility_set_mode(accessibility_mode);
|
||||
|
||||
// rendering_driver now held in static global String in main and initialized in setup()
|
||||
Error err;
|
||||
display_server = DisplayServer::create(display_driver_idx, rendering_driver, window_mode, window_vsync_mode, window_flags, window_position, window_size, init_screen, context, init_embed_parent_window_id, err);
|
||||
@ -4791,7 +4829,12 @@ bool Main::iteration() {
|
||||
return exit;
|
||||
}
|
||||
|
||||
OS::get_singleton()->add_frame_delay(DisplayServer::get_singleton()->window_can_draw());
|
||||
SceneTree *scene_tree = SceneTree::get_singleton();
|
||||
bool skip_delay = scene_tree && scene_tree->is_accessibility_enabled();
|
||||
|
||||
if (!skip_delay) {
|
||||
OS::get_singleton()->add_frame_delay(DisplayServer::get_singleton()->window_can_draw());
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (auto_build_solutions) {
|
||||
|
@ -31,3 +31,13 @@ GH-104890
|
||||
Validate extension JSON: API was removed: classes/JSONRPC/methods/set_scope
|
||||
|
||||
Replaced `set_scope` with `set_method`. Compatibility method registered for binary compatibility. Manual upgrade required by users to retain functionality.
|
||||
|
||||
|
||||
GH-76829
|
||||
--------
|
||||
Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/add_image/arguments': size changed value in new API, from 6 to 11.
|
||||
Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/add_image/arguments': size changed value in new API, from 10 to 11.
|
||||
Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/push_table/arguments': size changed value in new API, from 3 to 4.
|
||||
Validate extension JSON: Error: Field 'classes/TreeItem/methods/add_button/arguments': size changed value in new API, from 5 to 6.
|
||||
|
||||
Added optional arguments. Compatibility methods registered.
|
||||
|
@ -4210,6 +4210,8 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *
|
||||
p_shaped->uthk = 0.0;
|
||||
p_shaped->glyphs.clear();
|
||||
p_shaped->glyphs_logical.clear();
|
||||
p_shaped->runs.clear();
|
||||
p_shaped->runs_dirty = true;
|
||||
p_shaped->overrun_trim_data = TrimData();
|
||||
p_shaped->utf16 = Char16String();
|
||||
for (int i = 0; i < p_shaped->bidi_iter.size(); i++) {
|
||||
@ -4492,6 +4494,213 @@ Variant TextServerAdvanced::_shaped_get_span_embedded_object(const RID &p_shaped
|
||||
}
|
||||
}
|
||||
|
||||
String TextServerAdvanced::_shaped_get_span_text(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, String());
|
||||
ShapedTextDataAdvanced *span_sd = sd;
|
||||
if (sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ERR_FAIL_NULL_V(span_sd, String());
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, span_sd->spans.size(), String());
|
||||
return span_sd->text.substr(span_sd->spans[p_index].start, span_sd->spans[p_index].end - span_sd->spans[p_index].start);
|
||||
}
|
||||
|
||||
Variant TextServerAdvanced::_shaped_get_span_object(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, Variant());
|
||||
ShapedTextDataAdvanced *span_sd = sd;
|
||||
if (sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ERR_FAIL_NULL_V(span_sd, Variant());
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, span_sd->spans.size(), Variant());
|
||||
return span_sd->spans[p_index].embedded_key;
|
||||
}
|
||||
|
||||
void TextServerAdvanced::_generate_runs(ShapedTextDataAdvanced *p_sd) const {
|
||||
ERR_FAIL_NULL(p_sd);
|
||||
p_sd->runs.clear();
|
||||
|
||||
ShapedTextDataAdvanced *span_sd = p_sd;
|
||||
if (p_sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(p_sd->parent);
|
||||
ERR_FAIL_NULL(span_sd);
|
||||
}
|
||||
|
||||
int sd_size = p_sd->glyphs.size();
|
||||
const Glyph *sd_gl = p_sd->glyphs.ptr();
|
||||
|
||||
int span_count = span_sd->spans.size();
|
||||
int span = -1;
|
||||
int span_start = -1;
|
||||
int span_end = -1;
|
||||
|
||||
TextRun run;
|
||||
for (int i = 0; i < sd_size; i += sd_gl[i].count) {
|
||||
const Glyph &gl = sd_gl[i];
|
||||
if (gl.start < 0 || gl.end < 0) {
|
||||
continue;
|
||||
}
|
||||
if (gl.start < span_start || gl.start >= span_end) {
|
||||
span = -1;
|
||||
span_start = -1;
|
||||
span_end = -1;
|
||||
for (int j = 0; j < span_count; j++) {
|
||||
if (gl.start >= span_sd->spans[j].start && gl.end <= span_sd->spans[j].end) {
|
||||
span = j;
|
||||
span_start = span_sd->spans[j].start;
|
||||
span_end = span_sd->spans[j].end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (run.font_rid != gl.font_rid || run.font_size != gl.font_size || run.span_index != span || run.rtl != bool(gl.flags & GRAPHEME_IS_RTL)) {
|
||||
if (run.span_index >= 0) {
|
||||
p_sd->runs.push_back(run);
|
||||
}
|
||||
run.range = Vector2i(gl.start, gl.end);
|
||||
run.font_rid = gl.font_rid;
|
||||
run.font_size = gl.font_size;
|
||||
run.rtl = bool(gl.flags & GRAPHEME_IS_RTL);
|
||||
run.span_index = span;
|
||||
}
|
||||
run.range.x = MIN(run.range.x, gl.start);
|
||||
run.range.y = MAX(run.range.y, gl.end);
|
||||
}
|
||||
if (run.span_index >= 0) {
|
||||
p_sd->runs.push_back(run);
|
||||
}
|
||||
p_sd->runs_dirty = false;
|
||||
}
|
||||
|
||||
int64_t TextServerAdvanced::_shaped_get_run_count(const RID &p_shaped) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, 0);
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
return sd->runs.size();
|
||||
}
|
||||
|
||||
String TextServerAdvanced::_shaped_get_run_text(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, String());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), String());
|
||||
return sd->text.substr(sd->runs[p_index].range.x - sd->start, sd->runs[p_index].range.y - sd->runs[p_index].range.x);
|
||||
}
|
||||
|
||||
Vector2i TextServerAdvanced::_shaped_get_run_range(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, Vector2i());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), Vector2i());
|
||||
return sd->runs[p_index].range;
|
||||
}
|
||||
|
||||
RID TextServerAdvanced::_shaped_get_run_font_rid(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, RID());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), RID());
|
||||
return sd->runs[p_index].font_rid;
|
||||
}
|
||||
|
||||
int TextServerAdvanced::_shaped_get_run_font_size(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, 0);
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), 0);
|
||||
return sd->runs[p_index].font_size;
|
||||
}
|
||||
|
||||
String TextServerAdvanced::_shaped_get_run_language(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, String());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), String());
|
||||
|
||||
int span_idx = sd->runs[p_index].span_index;
|
||||
ShapedTextDataAdvanced *span_sd = sd;
|
||||
if (sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ERR_FAIL_NULL_V(span_sd, String());
|
||||
}
|
||||
ERR_FAIL_INDEX_V(span_idx, span_sd->spans.size(), String());
|
||||
return span_sd->spans[span_idx].language;
|
||||
}
|
||||
|
||||
TextServer::Direction TextServerAdvanced::_shaped_get_run_direction(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, TextServer::DIRECTION_LTR);
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), TextServer::DIRECTION_LTR);
|
||||
return sd->runs[p_index].rtl ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR;
|
||||
}
|
||||
|
||||
Variant TextServerAdvanced::_shaped_get_run_object(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, Variant());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerAdvanced *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), Variant());
|
||||
|
||||
int span_idx = sd->runs[p_index].span_index;
|
||||
ShapedTextDataAdvanced *span_sd = sd;
|
||||
if (sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ERR_FAIL_NULL_V(span_sd, Variant());
|
||||
}
|
||||
ERR_FAIL_INDEX_V(span_idx, span_sd->spans.size(), Variant());
|
||||
return span_sd->spans[span_idx].embedded_key;
|
||||
}
|
||||
|
||||
void TextServerAdvanced::_shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL(sd);
|
||||
@ -4575,6 +4784,13 @@ bool TextServerAdvanced::_shaped_text_add_object(const RID &p_shaped, const Vari
|
||||
return true;
|
||||
}
|
||||
|
||||
String TextServerAdvanced::_shaped_get_text(const RID &p_shaped) const {
|
||||
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, String());
|
||||
|
||||
return sd->text;
|
||||
}
|
||||
|
||||
bool TextServerAdvanced::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) {
|
||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, false);
|
||||
@ -4771,6 +4987,8 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
|
||||
p_new_sd->sort_valid = false;
|
||||
p_new_sd->upos = p_sd->upos;
|
||||
p_new_sd->uthk = p_sd->uthk;
|
||||
p_new_sd->runs.clear();
|
||||
p_new_sd->runs_dirty = true;
|
||||
|
||||
if (p_length > 0) {
|
||||
p_new_sd->text = p_sd->text.substr(p_start - p_sd->start, p_length);
|
||||
@ -6560,6 +6778,8 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
|
||||
} else {
|
||||
bidi_ranges = sd->bidi_override;
|
||||
}
|
||||
sd->runs.clear();
|
||||
sd->runs_dirty = true;
|
||||
|
||||
for (int ov = 0; ov < bidi_ranges.size(); ov++) {
|
||||
// Create BiDi iterator.
|
||||
|
@ -458,6 +458,14 @@ class TextServerAdvanced : public TextServerExtension {
|
||||
Vector<Glyph> ellipsis_glyph_buf;
|
||||
};
|
||||
|
||||
struct TextRun {
|
||||
Vector2i range;
|
||||
RID font_rid;
|
||||
int font_size = 0;
|
||||
bool rtl = false;
|
||||
int64_t span_index = -1;
|
||||
};
|
||||
|
||||
struct ShapedTextDataAdvanced {
|
||||
Mutex mutex;
|
||||
|
||||
@ -489,6 +497,9 @@ class TextServerAdvanced : public TextServerExtension {
|
||||
int first_span = 0; // First span in the parent ShapedTextData.
|
||||
int last_span = 0;
|
||||
|
||||
Vector<TextRun> runs;
|
||||
bool runs_dirty = true;
|
||||
|
||||
struct EmbeddedObject {
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
@ -664,6 +675,7 @@ class TextServerAdvanced : public TextServerExtension {
|
||||
mutable HashMap<String, PackedByteArray> system_font_data;
|
||||
|
||||
void _update_chars(ShapedTextDataAdvanced *p_sd) const;
|
||||
void _generate_runs(ShapedTextDataAdvanced *p_sd) const;
|
||||
void _realign(ShapedTextDataAdvanced *p_sd) const;
|
||||
int64_t _convert_pos(const String &p_utf32, const Char16String &p_utf16, int64_t p_pos) const;
|
||||
int64_t _convert_pos(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const;
|
||||
@ -956,12 +968,24 @@ public:
|
||||
MODBIND7R(bool, shaped_text_add_string, const RID &, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &);
|
||||
MODBIND6R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t, double);
|
||||
MODBIND5R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment, double);
|
||||
MODBIND1RC(String, shaped_get_text, const RID &);
|
||||
|
||||
MODBIND1RC(int64_t, shaped_get_span_count, const RID &);
|
||||
MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t);
|
||||
MODBIND2RC(Variant, shaped_get_span_embedded_object, const RID &, int64_t);
|
||||
MODBIND2RC(String, shaped_get_span_text, const RID &, int64_t);
|
||||
MODBIND2RC(Variant, shaped_get_span_object, const RID &, int64_t);
|
||||
MODBIND5(shaped_set_span_update_font, const RID &, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &);
|
||||
|
||||
MODBIND1RC(int64_t, shaped_get_run_count, const RID &);
|
||||
MODBIND2RC(String, shaped_get_run_text, const RID &, int64_t);
|
||||
MODBIND2RC(Vector2i, shaped_get_run_range, const RID &, int64_t);
|
||||
MODBIND2RC(RID, shaped_get_run_font_rid, const RID &, int64_t);
|
||||
MODBIND2RC(int, shaped_get_run_font_size, const RID &, int64_t);
|
||||
MODBIND2RC(String, shaped_get_run_language, const RID &, int64_t);
|
||||
MODBIND2RC(Direction, shaped_get_run_direction, const RID &, int64_t);
|
||||
MODBIND2RC(Variant, shaped_get_run_object, const RID &, int64_t);
|
||||
|
||||
MODBIND3RC(RID, shaped_text_substr, const RID &, int64_t, int64_t);
|
||||
MODBIND1RC(RID, shaped_text_get_parent, const RID &);
|
||||
|
||||
|
@ -3105,6 +3105,8 @@ void TextServerFallback::invalidate(ShapedTextDataFallback *p_shaped) {
|
||||
p_shaped->uthk = 0.0;
|
||||
p_shaped->glyphs.clear();
|
||||
p_shaped->glyphs_logical.clear();
|
||||
p_shaped->runs.clear();
|
||||
p_shaped->runs_dirty = true;
|
||||
}
|
||||
|
||||
void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
|
||||
@ -3339,6 +3341,212 @@ Variant TextServerFallback::_shaped_get_span_embedded_object(const RID &p_shaped
|
||||
}
|
||||
}
|
||||
|
||||
String TextServerFallback::_shaped_get_span_text(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, String());
|
||||
ShapedTextDataFallback *span_sd = sd;
|
||||
if (sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ERR_FAIL_NULL_V(span_sd, String());
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, span_sd->spans.size(), String());
|
||||
return span_sd->text.substr(span_sd->spans[p_index].start, span_sd->spans[p_index].end - span_sd->spans[p_index].start);
|
||||
}
|
||||
|
||||
Variant TextServerFallback::_shaped_get_span_object(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, Variant());
|
||||
ShapedTextDataFallback *span_sd = sd;
|
||||
if (sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ERR_FAIL_NULL_V(span_sd, Variant());
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, span_sd->spans.size(), Variant());
|
||||
return span_sd->spans[p_index].embedded_key;
|
||||
}
|
||||
|
||||
void TextServerFallback::_generate_runs(ShapedTextDataFallback *p_sd) const {
|
||||
ERR_FAIL_NULL(p_sd);
|
||||
p_sd->runs.clear();
|
||||
|
||||
ShapedTextDataFallback *span_sd = p_sd;
|
||||
if (p_sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(p_sd->parent);
|
||||
ERR_FAIL_NULL(span_sd);
|
||||
}
|
||||
|
||||
int sd_size = p_sd->glyphs.size();
|
||||
Glyph *sd_gl = p_sd->glyphs.ptrw();
|
||||
|
||||
int span_count = span_sd->spans.size();
|
||||
int span = -1;
|
||||
int span_start = -1;
|
||||
int span_end = -1;
|
||||
|
||||
TextRun run;
|
||||
for (int i = 0; i < sd_size; i += sd_gl[i].count) {
|
||||
const Glyph &gl = sd_gl[i];
|
||||
if (gl.start < 0 || gl.end < 0) {
|
||||
continue;
|
||||
}
|
||||
if (gl.start < span_start || gl.start >= span_end) {
|
||||
span = -1;
|
||||
span_start = -1;
|
||||
span_end = -1;
|
||||
for (int j = 0; j < span_count; j++) {
|
||||
if (gl.start >= span_sd->spans[j].start && gl.end <= span_sd->spans[j].end) {
|
||||
span = j;
|
||||
span_start = span_sd->spans[j].start;
|
||||
span_end = span_sd->spans[j].end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (run.font_rid != gl.font_rid || run.font_size != gl.font_size || run.span_index != span) {
|
||||
if (run.span_index >= 0) {
|
||||
p_sd->runs.push_back(run);
|
||||
}
|
||||
run.range = Vector2i(gl.start, gl.end);
|
||||
run.font_rid = gl.font_rid;
|
||||
run.font_size = gl.font_size;
|
||||
run.span_index = span;
|
||||
}
|
||||
run.range.x = MIN(run.range.x, gl.start);
|
||||
run.range.y = MAX(run.range.y, gl.end);
|
||||
}
|
||||
if (run.span_index >= 0) {
|
||||
p_sd->runs.push_back(run);
|
||||
}
|
||||
p_sd->runs_dirty = false;
|
||||
}
|
||||
|
||||
int64_t TextServerFallback::_shaped_get_run_count(const RID &p_shaped) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, 0);
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
return sd->runs.size();
|
||||
}
|
||||
|
||||
String TextServerFallback::_shaped_get_run_text(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, String());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), String());
|
||||
return sd->text.substr(sd->runs[p_index].range.x - sd->start, sd->runs[p_index].range.y - sd->runs[p_index].range.x);
|
||||
}
|
||||
|
||||
Vector2i TextServerFallback::_shaped_get_run_range(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, Vector2i());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), Vector2i());
|
||||
return sd->runs[p_index].range;
|
||||
}
|
||||
|
||||
RID TextServerFallback::_shaped_get_run_font_rid(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, RID());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), RID());
|
||||
return sd->runs[p_index].font_rid;
|
||||
}
|
||||
|
||||
int TextServerFallback::_shaped_get_run_font_size(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, 0);
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), 0);
|
||||
return sd->runs[p_index].font_size;
|
||||
}
|
||||
|
||||
String TextServerFallback::_shaped_get_run_language(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, String());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), String());
|
||||
|
||||
int span_idx = sd->runs[p_index].span_index;
|
||||
ShapedTextDataFallback *span_sd = sd;
|
||||
if (sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ERR_FAIL_NULL_V(span_sd, String());
|
||||
}
|
||||
ERR_FAIL_INDEX_V(span_idx, span_sd->spans.size(), String());
|
||||
return span_sd->spans[span_idx].language;
|
||||
}
|
||||
|
||||
TextServer::Direction TextServerFallback::_shaped_get_run_direction(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, TextServer::DIRECTION_LTR);
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), TextServer::DIRECTION_LTR);
|
||||
return TextServer::DIRECTION_LTR;
|
||||
}
|
||||
|
||||
Variant TextServerFallback::_shaped_get_run_object(const RID &p_shaped, int64_t p_index) const {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, Variant());
|
||||
MutexLock lock(sd->mutex);
|
||||
if (!sd->valid.is_set()) {
|
||||
const_cast<TextServerFallback *>(this)->_shaped_text_shape(p_shaped);
|
||||
}
|
||||
if (sd->runs_dirty) {
|
||||
_generate_runs(sd);
|
||||
}
|
||||
ERR_FAIL_INDEX_V(p_index, sd->runs.size(), Variant());
|
||||
|
||||
int span_idx = sd->runs[p_index].span_index;
|
||||
ShapedTextDataFallback *span_sd = sd;
|
||||
if (sd->parent.is_valid()) {
|
||||
span_sd = shaped_owner.get_or_null(sd->parent);
|
||||
ERR_FAIL_NULL_V(span_sd, Variant());
|
||||
}
|
||||
ERR_FAIL_INDEX_V(span_idx, span_sd->spans.size(), Variant());
|
||||
return span_sd->spans[span_idx].embedded_key;
|
||||
}
|
||||
|
||||
void TextServerFallback::_shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL(sd);
|
||||
@ -3450,6 +3658,13 @@ bool TextServerFallback::_shaped_text_add_object(const RID &p_shaped, const Vari
|
||||
return true;
|
||||
}
|
||||
|
||||
String TextServerFallback::_shaped_get_text(const RID &p_shaped) const {
|
||||
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, String());
|
||||
|
||||
return sd->text;
|
||||
}
|
||||
|
||||
bool TextServerFallback::_shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) {
|
||||
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
|
||||
ERR_FAIL_NULL_V(sd, false);
|
||||
@ -4385,6 +4600,8 @@ bool TextServerFallback::_shaped_text_shape(const RID &p_shaped) {
|
||||
sd->descent = 0.0;
|
||||
sd->width = 0.0;
|
||||
sd->glyphs.clear();
|
||||
sd->runs.clear();
|
||||
sd->runs_dirty = true;
|
||||
|
||||
if (sd->text.length() == 0) {
|
||||
sd->valid.set();
|
||||
|
@ -400,6 +400,13 @@ class TextServerFallback : public TextServerExtension {
|
||||
Vector<Glyph> ellipsis_glyph_buf;
|
||||
};
|
||||
|
||||
struct TextRun {
|
||||
Vector2i range;
|
||||
RID font_rid;
|
||||
int font_size = 0;
|
||||
int64_t span_index = -1;
|
||||
};
|
||||
|
||||
struct ShapedTextDataFallback {
|
||||
Mutex mutex;
|
||||
|
||||
@ -431,6 +438,9 @@ class TextServerFallback : public TextServerExtension {
|
||||
int first_span = 0; // First span in the parent ShapedTextData.
|
||||
int last_span = 0;
|
||||
|
||||
Vector<TextRun> runs;
|
||||
bool runs_dirty = true;
|
||||
|
||||
struct EmbeddedObject {
|
||||
int start = -1;
|
||||
int end = -1;
|
||||
@ -575,6 +585,7 @@ class TextServerFallback : public TextServerExtension {
|
||||
mutable HashMap<SystemFontKey, SystemFontCache, SystemFontKeyHasher> system_fonts;
|
||||
mutable HashMap<String, PackedByteArray> system_font_data;
|
||||
|
||||
void _generate_runs(ShapedTextDataFallback *p_sd) const;
|
||||
void _realign(ShapedTextDataFallback *p_sd) const;
|
||||
_FORCE_INLINE_ RID _find_sys_font_for_text(const RID &p_fdef, const String &p_script_code, const String &p_language, const String &p_text);
|
||||
|
||||
@ -820,12 +831,24 @@ public:
|
||||
MODBIND7R(bool, shaped_text_add_string, const RID &, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &);
|
||||
MODBIND6R(bool, shaped_text_add_object, const RID &, const Variant &, const Size2 &, InlineAlignment, int64_t, double);
|
||||
MODBIND5R(bool, shaped_text_resize_object, const RID &, const Variant &, const Size2 &, InlineAlignment, double);
|
||||
MODBIND1RC(String, shaped_get_text, const RID &);
|
||||
|
||||
MODBIND1RC(int64_t, shaped_get_span_count, const RID &);
|
||||
MODBIND2RC(Variant, shaped_get_span_meta, const RID &, int64_t);
|
||||
MODBIND2RC(Variant, shaped_get_span_embedded_object, const RID &, int64_t);
|
||||
MODBIND2RC(String, shaped_get_span_text, const RID &, int64_t);
|
||||
MODBIND2RC(Variant, shaped_get_span_object, const RID &, int64_t);
|
||||
MODBIND5(shaped_set_span_update_font, const RID &, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &);
|
||||
|
||||
MODBIND1RC(int64_t, shaped_get_run_count, const RID &);
|
||||
MODBIND2RC(String, shaped_get_run_text, const RID &, int64_t);
|
||||
MODBIND2RC(Vector2i, shaped_get_run_range, const RID &, int64_t);
|
||||
MODBIND2RC(RID, shaped_get_run_font_rid, const RID &, int64_t);
|
||||
MODBIND2RC(int, shaped_get_run_font_size, const RID &, int64_t);
|
||||
MODBIND2RC(String, shaped_get_run_language, const RID &, int64_t);
|
||||
MODBIND2RC(Direction, shaped_get_run_direction, const RID &, int64_t);
|
||||
MODBIND2RC(Variant, shaped_get_run_object, const RID &, int64_t);
|
||||
|
||||
MODBIND3RC(RID, shaped_text_substr, const RID &, int64_t, int64_t);
|
||||
MODBIND1RC(RID, shaped_text_get_parent, const RID &);
|
||||
|
||||
|
@ -167,6 +167,17 @@ void AnimatedSprite2D::_validate_property(PropertyInfo &p_property) const {
|
||||
|
||||
void AnimatedSprite2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
Rect2 dst_rect = _get_rect();
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_IMAGE);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_transform(ae, get_transform());
|
||||
DisplayServer::get_singleton()->accessibility_update_set_bounds(ae, dst_rect);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_READY: {
|
||||
if (!Engine::get_singleton()->is_editor_hint() && frames.is_valid() && frames->has_animation(autoplay)) {
|
||||
play(autoplay);
|
||||
|
@ -388,11 +388,11 @@ PackedStringArray GPUParticles2D::get_configuration_warnings() const {
|
||||
}
|
||||
}
|
||||
|
||||
if (trail_enabled && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
if (trail_enabled && (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" || OS::get_singleton()->get_current_rendering_method() == "dummy")) {
|
||||
warnings.push_back(RTR("Particle trails are only available when using the Forward+ or Mobile renderers."));
|
||||
}
|
||||
|
||||
if (sub_emitter != NodePath() && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
if (sub_emitter != NodePath() && (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" || OS::get_singleton()->get_current_rendering_method() == "dummy")) {
|
||||
warnings.push_back(RTR("Particle sub-emitters are not available when using the Compatibility renderer."));
|
||||
}
|
||||
|
||||
|
@ -429,6 +429,13 @@ Point2 Node2D::to_global(Point2 p_local) const {
|
||||
|
||||
void Node2D::_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_CONTAINER);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
|
||||
|
@ -112,8 +112,27 @@ bool TouchScreenButton::is_shape_centered() const {
|
||||
return shape_centered;
|
||||
}
|
||||
|
||||
void TouchScreenButton::_accessibility_action_click(const Variant &p_data) {
|
||||
_press(0);
|
||||
_release();
|
||||
}
|
||||
|
||||
void TouchScreenButton::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
Rect2 dst_rect(Point2(), texture_normal.is_valid() ? texture_normal->get_size() : Size2());
|
||||
|
||||
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, &TouchScreenButton::_accessibility_action_click));
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_transform(ae, get_transform());
|
||||
DisplayServer::get_singleton()->accessibility_update_set_bounds(ae, dst_rect);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
|
@ -74,6 +74,8 @@ protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
void _accessibility_action_click(const Variant &p_data);
|
||||
|
||||
public:
|
||||
#ifdef DEBUG_ENABLED
|
||||
virtual Rect2 _edit_get_rect() const override;
|
||||
|
@ -115,6 +115,17 @@ void Sprite2D::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_c
|
||||
|
||||
void Sprite2D::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ACCESSIBILITY_UPDATE: {
|
||||
RID ae = get_accessibility_element();
|
||||
ERR_FAIL_COND(ae.is_null());
|
||||
|
||||
Rect2 dst_rect = get_rect();
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_IMAGE);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_transform(ae, get_transform());
|
||||
DisplayServer::get_singleton()->accessibility_update_set_bounds(ae, dst_rect);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
if (texture.is_null()) {
|
||||
return;
|
||||
|
@ -179,7 +179,7 @@ void Decal::_validate_property(PropertyInfo &p_property) const {
|
||||
PackedStringArray Decal::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = VisualInstance3D::get_configuration_warnings();
|
||||
|
||||
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" || OS::get_singleton()->get_current_rendering_method() == "dummy") {
|
||||
warnings.push_back(RTR("Decals are only available when using the Forward+ or Mobile renderers."));
|
||||
return warnings;
|
||||
}
|
||||
|
@ -421,12 +421,12 @@ PackedStringArray GPUParticles3D::get_configuration_warnings() const {
|
||||
if ((dp_count || skin.is_valid()) && (missing_trails || no_materials)) {
|
||||
warnings.push_back(RTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering."));
|
||||
}
|
||||
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" || OS::get_singleton()->get_current_rendering_method() == "dummy") {
|
||||
warnings.push_back(RTR("Particle trails are only available when using the Forward+ or Mobile renderers."));
|
||||
}
|
||||
}
|
||||
|
||||
if (sub_emitter != NodePath() && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
if (sub_emitter != NodePath() && (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" || OS::get_singleton()->get_current_rendering_method() == "dummy")) {
|
||||
warnings.push_back(RTR("Particle sub-emitters are only available when using the Forward+ or Mobile renderers."));
|
||||
}
|
||||
|
||||
|
@ -632,7 +632,7 @@ PackedStringArray OmniLight3D::get_configuration_warnings() const {
|
||||
warnings.push_back(RTR("Projector texture only works with shadows active."));
|
||||
}
|
||||
|
||||
if (get_projector().is_valid() && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
if (get_projector().is_valid() && (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" || OS::get_singleton()->get_current_rendering_method() == "dummy")) {
|
||||
warnings.push_back(RTR("Projector textures are not supported when using the Compatibility renderer yet. Support will be added in a future release."));
|
||||
}
|
||||
|
||||
@ -668,7 +668,7 @@ PackedStringArray SpotLight3D::get_configuration_warnings() const {
|
||||
warnings.push_back(RTR("Projector texture only works with shadows active."));
|
||||
}
|
||||
|
||||
if (get_projector().is_valid() && OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
if (get_projector().is_valid() && (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" || OS::get_singleton()->get_current_rendering_method() == "dummy")) {
|
||||
warnings.push_back(RTR("Projector textures are not supported when using the Compatibility renderer yet. Support will be added in a future release."));
|
||||
}
|
||||
|
||||
|
@ -131,6 +131,13 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) {
|
||||
|
||||
void Node3D::_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_ENTER_TREE: {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
ERR_FAIL_NULL(get_tree());
|
||||
|
@ -542,6 +542,8 @@ PackedStringArray VoxelGI::get_configuration_warnings() const {
|
||||
|
||||
if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
|
||||
warnings.push_back(RTR("VoxelGI nodes are not supported when using the Compatibility renderer yet. Support will be added in a future release."));
|
||||
} else if (OS::get_singleton()->get_current_rendering_method() == "dummy") {
|
||||
warnings.push_back(RTR("VoxelGI nodes are not supported when using the Dummy renderer."));
|
||||
} else if (probe_data.is_null()) {
|
||||
warnings.push_back(RTR("No VoxelGI data set, so this node is disabled. Bake static objects to enable GI."));
|
||||
}
|
||||
|
@ -35,7 +35,14 @@
|
||||
#include "servers/audio/audio_stream.h"
|
||||
|
||||
void AudioStreamPlayer::_notification(int p_what) {
|
||||
internal->notification(p_what);
|
||||
if (p_what == 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_AUDIO);
|
||||
} else {
|
||||
internal->notification(p_what);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamPlayer::set_stream(Ref<AudioStream> p_stream) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -86,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)
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
#include "button.h"
|
||||
|
||||
#include "scene/gui/dialogs.h"
|
||||
|
||||
#include "scene/theme/theme_db.h"
|
||||
|
||||
Size2 Button::get_minimum_size() const {
|
||||
@ -185,6 +187,21 @@ 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;
|
||||
@ -194,6 +211,7 @@ void Button::_notification(int p_what) {
|
||||
_shape();
|
||||
|
||||
update_minimum_size();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
} break;
|
||||
|
||||
@ -610,6 +628,7 @@ void Button::set_text(const String &p_text) {
|
||||
xl_text = translated_text;
|
||||
_shape();
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
}
|
||||
@ -649,6 +668,7 @@ void Button::set_text_direction(Control::TextDirection p_text_direction) {
|
||||
if (text_direction != p_text_direction) {
|
||||
text_direction = p_text_direction;
|
||||
_shape();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -661,6 +681,7 @@ void Button::set_language(const String &p_language) {
|
||||
if (language != p_language) {
|
||||
language = p_language;
|
||||
_shape();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -738,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();
|
||||
}
|
||||
}
|
||||
|
@ -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: {
|
||||
@ -134,7 +145,7 @@ void CheckBox::_notification(int p_what) {
|
||||
}
|
||||
}
|
||||
|
||||
bool CheckBox::is_radio() {
|
||||
bool CheckBox::is_radio() const {
|
||||
return get_button_group().is_valid();
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
bool is_radio();
|
||||
bool is_radio() const;
|
||||
|
||||
public:
|
||||
CheckBox(const String &p_text = String());
|
||||
|
@ -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: {
|
||||
|
@ -56,20 +56,31 @@
|
||||
|
||||
void ColorPicker::_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_COLOR_PICKER);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_color_value(ae, color);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
_update_color();
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_READY: {
|
||||
// FIXME: The embedding check is needed to fix a bug in single-window mode (GH-93718).
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_COLOR_PICKER)) {
|
||||
btn_pick->set_accessibility_name(ETR("Pick Color From Screen"));
|
||||
btn_pick->set_tooltip_text(ETR("Pick a color from the screen."));
|
||||
btn_pick->connect(SceneStringName(pressed), callable_mp(this, &ColorPicker::_pick_button_pressed_native));
|
||||
} else if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SCREEN_CAPTURE) && !get_tree()->get_root()->is_embedding_subwindows()) {
|
||||
// FIXME: The embedding check is needed to fix a bug in single-window mode (GH-93718).
|
||||
btn_pick->set_accessibility_name(ETR("Pick Color From Screen"));
|
||||
btn_pick->set_tooltip_text(ETR("Pick a color from the screen."));
|
||||
btn_pick->connect(SceneStringName(pressed), callable_mp(this, &ColorPicker::_pick_button_pressed));
|
||||
} else {
|
||||
// On unsupported platforms, use a legacy method for color picking.
|
||||
btn_pick->set_accessibility_name(ETR("Pick Color From Window"));
|
||||
btn_pick->set_tooltip_text(ETR("Pick a color from the application window."));
|
||||
btn_pick->connect(SceneStringName(pressed), callable_mp(this, &ColorPicker::_pick_button_pressed_legacy));
|
||||
}
|
||||
@ -338,8 +349,12 @@ void ColorPicker::_update_controls() {
|
||||
|
||||
for (int i = 0; i < current_slider_count; i++) {
|
||||
labels[i]->set_text(modes[current_mode]->get_slider_label(i));
|
||||
sliders[i]->set_accessibility_name(modes[current_mode]->get_slider_label(i));
|
||||
values[i]->set_accessibility_name(modes[current_mode]->get_slider_label(i));
|
||||
}
|
||||
alpha_label->set_text("A");
|
||||
alpha_slider->set_accessibility_name(ETR("Alpha"));
|
||||
alpha_value->set_accessibility_name(ETR("Alpha"));
|
||||
|
||||
slider_theme_modified = modes[current_mode]->apply_theme();
|
||||
|
||||
@ -726,6 +741,7 @@ void ColorPicker::_update_color(bool p_update_sliders) {
|
||||
}
|
||||
alpha_slider->queue_redraw();
|
||||
updating = false;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void ColorPicker::_update_presets() {
|
||||
@ -862,6 +878,7 @@ inline int ColorPicker::_get_preset_size() {
|
||||
void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
|
||||
ColorPresetButton *btn_preset_new = memnew(ColorPresetButton(p_color, p_size));
|
||||
btn_preset_new->set_tooltip_text(vformat(atr(ETR("Color: #%s\nLMB: Apply color\nRMB: Remove preset")), p_color.to_html(p_color.a < 1)));
|
||||
btn_preset_new->set_accessibility_name(vformat(atr(ETR("Color: #%")), p_color.to_html(p_color.a < 1)));
|
||||
SET_DRAG_FORWARDING_GCDU(btn_preset_new, ColorPicker);
|
||||
btn_preset_new->set_button_group(preset_group);
|
||||
preset_container->add_child(btn_preset_new);
|
||||
@ -872,6 +889,7 @@ void ColorPicker::_add_preset_button(int p_size, const Color &p_color) {
|
||||
void ColorPicker::_add_recent_preset_button(int p_size, const Color &p_color) {
|
||||
ColorPresetButton *btn_preset_new = memnew(ColorPresetButton(p_color, p_size));
|
||||
btn_preset_new->set_tooltip_text(vformat(atr(ETR("Color: #%s\nLMB: Apply color")), p_color.to_html(p_color.a < 1)));
|
||||
btn_preset_new->set_accessibility_name(vformat(atr(ETR("Color: #%s")), p_color.to_html(p_color.a < 1)));
|
||||
btn_preset_new->set_button_group(recent_preset_group);
|
||||
recent_preset_hbc->add_child(btn_preset_new);
|
||||
recent_preset_hbc->move_child(btn_preset_new, 0);
|
||||
@ -2000,6 +2018,7 @@ ColorPicker::ColorPicker() {
|
||||
|
||||
btn_pick = memnew(Button);
|
||||
btn_pick->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
btn_pick->set_accessibility_name(ETR("Pick"));
|
||||
sample_hbc->add_child(btn_pick);
|
||||
|
||||
sample = memnew(TextureRect);
|
||||
@ -2012,6 +2031,7 @@ ColorPicker::ColorPicker() {
|
||||
btn_shape->set_flat(false);
|
||||
sample_hbc->add_child(btn_shape);
|
||||
btn_shape->set_toggle_mode(true);
|
||||
btn_shape->set_accessibility_name(ETR("Picker Shape"));
|
||||
btn_shape->set_tooltip_text(ETR("Select a picker shape."));
|
||||
btn_shape->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
btn_shape->set_focus_mode(FOCUS_ALL);
|
||||
@ -2061,6 +2081,7 @@ ColorPicker::ColorPicker() {
|
||||
btn_mode->set_flat(false);
|
||||
mode_hbc->add_child(btn_mode);
|
||||
btn_mode->set_toggle_mode(true);
|
||||
btn_mode->set_accessibility_name(ETR("Picker Mode"));
|
||||
btn_mode->set_tooltip_text(ETR("Select a picker mode."));
|
||||
btn_mode->set_focus_mode(FOCUS_ALL);
|
||||
|
||||
@ -2101,7 +2122,8 @@ ColorPicker::ColorPicker() {
|
||||
text_type = memnew(Button);
|
||||
hex_hbc->add_child(text_type);
|
||||
text_type->set_text("#");
|
||||
text_type->set_tooltip_text(RTR("Switch between hexadecimal and code values."));
|
||||
text_type->set_accessibility_name(ETR("Hexadecimal/Code Values"));
|
||||
text_type->set_tooltip_text(ETR("Switch between hexadecimal and code values."));
|
||||
if (Engine::get_singleton()->is_editor_hint()) {
|
||||
text_type->connect(SceneStringName(pressed), callable_mp(this, &ColorPicker::_text_type_toggled));
|
||||
} else {
|
||||
@ -2114,6 +2136,7 @@ ColorPicker::ColorPicker() {
|
||||
hex_hbc->add_child(c_text);
|
||||
c_text->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||
c_text->set_select_all_on_focus(true);
|
||||
c_text->set_accessibility_name(ETR("Hex Code or Name"));
|
||||
c_text->set_tooltip_text(ETR("Enter a hex code (\"#ff0000\") or named color (\"red\")."));
|
||||
c_text->set_placeholder(ETR("Hex code or named color"));
|
||||
c_text->connect(SceneStringName(text_submitted), callable_mp(this, &ColorPicker::_html_submitted));
|
||||
@ -2151,6 +2174,7 @@ ColorPicker::ColorPicker() {
|
||||
menu_btn->set_flat(true);
|
||||
menu_btn->set_focus_mode(FOCUS_ALL);
|
||||
menu_btn->set_tooltip_text(ETR("Show all options available."));
|
||||
menu_btn->set_accessibility_name(ETR("All Options"));
|
||||
menu_btn->connect("about_to_popup", callable_mp(this, &ColorPicker::_update_menu_items));
|
||||
palette_box->add_child(menu_btn);
|
||||
|
||||
@ -2187,6 +2211,7 @@ ColorPicker::ColorPicker() {
|
||||
btn_add_preset = memnew(Button);
|
||||
btn_add_preset->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
|
||||
btn_add_preset->set_tooltip_text(ETR("Add current color as a preset."));
|
||||
btn_add_preset->set_accessibility_name(ETR("Add Preset"));
|
||||
btn_add_preset->connect(SceneStringName(pressed), callable_mp(this, &ColorPicker::_add_preset_pressed));
|
||||
preset_container->add_child(btn_add_preset);
|
||||
}
|
||||
@ -2223,6 +2248,7 @@ void ColorPickerButton::_about_to_popup() {
|
||||
|
||||
void ColorPickerButton::_color_changed(const Color &p_color) {
|
||||
color = p_color;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
emit_signal(SNAME("color_changed"), color);
|
||||
}
|
||||
@ -2269,6 +2295,15 @@ void ColorPickerButton::pressed() {
|
||||
|
||||
void ColorPickerButton::_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_DIALOG);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_color_value(ae, color);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
const Rect2 r = Rect2(theme_cache.normal_style->get_offset(), get_size() - theme_cache.normal_style->get_minimum_size());
|
||||
draw_texture_rect(theme_cache.background_icon, r, true);
|
||||
@ -2302,7 +2337,7 @@ void ColorPickerButton::set_pick_color(const Color &p_color) {
|
||||
if (picker) {
|
||||
picker->set_pick_color(p_color);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -2382,6 +2417,14 @@ ColorPickerButton::ColorPickerButton(const String &p_text) :
|
||||
|
||||
void ColorPresetButton::_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_color_value(ae, preset_color);
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_DRAW: {
|
||||
const Rect2 r = Rect2(Point2(0, 0), get_size());
|
||||
Ref<StyleBox> sb_raw = theme_cache.foreground_style->duplicate();
|
||||
@ -2439,6 +2482,7 @@ void ColorPresetButton::_notification(int p_what) {
|
||||
|
||||
void ColorPresetButton::set_preset_color(const Color &p_color) {
|
||||
preset_color = p_color;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
Color ColorPresetButton::get_preset_color() const {
|
||||
|
@ -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;
|
||||
|
@ -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: {
|
||||
|
@ -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"
|
||||
@ -246,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;
|
||||
@ -1534,6 +1559,7 @@ void Control::set_scale(const Vector2 &p_scale) {
|
||||
}
|
||||
queue_redraw();
|
||||
_notify_transform();
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
Vector2 Control::get_scale() const {
|
||||
@ -1550,6 +1576,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) {
|
||||
@ -1576,6 +1603,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 {
|
||||
@ -1749,6 +1777,8 @@ void Control::_size_changed() {
|
||||
if (pos_changed && !size_changed) {
|
||||
_update_canvas_item_transform();
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
} else if (pos_changed) {
|
||||
_notify_transform();
|
||||
}
|
||||
@ -2018,7 +2048,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(INFINITY, INFINITY));
|
||||
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(INFINITY, INFINITY), this);
|
||||
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void Control::set_drag_preview(Control *p_control) {
|
||||
@ -2037,7 +2100,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();
|
||||
@ -2163,6 +2226,10 @@ Control *Control::find_next_valid_focus() const {
|
||||
}
|
||||
|
||||
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) {
|
||||
// Find next child.
|
||||
@ -2194,6 +2261,25 @@ Control *Control::find_next_valid_focus() const {
|
||||
}
|
||||
next_child = next_child->data.parent_control;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
window_next = next;
|
||||
next_child = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2201,7 +2287,7 @@ Control *Control::find_next_valid_focus() const {
|
||||
break;
|
||||
}
|
||||
|
||||
if (next_child->get_focus_mode_with_recursive() == FOCUS_ALL) {
|
||||
if ((next_child->get_focus_mode_with_recursive() == FOCUS_ALL) || (ac_enabled && next_child->get_focus_mode_with_recursive() == FOCUS_ACCESSIBILITY)) {
|
||||
return next_child;
|
||||
}
|
||||
|
||||
@ -2244,6 +2330,10 @@ Control *Control::find_prev_valid_focus() const {
|
||||
}
|
||||
|
||||
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) {
|
||||
// Find prev child.
|
||||
@ -2253,7 +2343,28 @@ Control *Control::find_prev_valid_focus() const {
|
||||
if (from->is_set_as_top_level() || !from->data.parent_control) {
|
||||
// Find last of the children.
|
||||
|
||||
prev_child = _prev_control(from); // Wrap start here.
|
||||
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--) {
|
||||
@ -2274,7 +2385,7 @@ Control *Control::find_prev_valid_focus() const {
|
||||
}
|
||||
}
|
||||
|
||||
if (prev_child->get_focus_mode_with_recursive() == 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;
|
||||
}
|
||||
|
||||
@ -2509,11 +2620,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_with_recursive() == 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());
|
||||
@ -3419,6 +3532,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;
|
||||
@ -3428,6 +3548,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) {
|
||||
@ -3439,6 +3588,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(INFINITY, INFINITY), 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;
|
||||
|
||||
@ -3773,6 +3947,9 @@ 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);
|
||||
@ -3874,7 +4051,7 @@ 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_");
|
||||
@ -3893,6 +4070,7 @@ 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);
|
||||
@ -4003,6 +4181,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");
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,8 @@ public:
|
||||
enum FocusMode {
|
||||
FOCUS_NONE,
|
||||
FOCUS_CLICK,
|
||||
FOCUS_ALL
|
||||
FOCUS_ALL,
|
||||
FOCUS_ACCESSIBILITY,
|
||||
};
|
||||
|
||||
enum RecursiveBehavior {
|
||||
@ -345,8 +346,6 @@ private:
|
||||
|
||||
static int root_layout_direction;
|
||||
|
||||
String get_tooltip_text() const;
|
||||
|
||||
protected:
|
||||
// Dynamic properties.
|
||||
|
||||
@ -371,6 +370,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)
|
||||
@ -383,6 +388,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:
|
||||
@ -438,6 +445,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
|
||||
@ -556,6 +564,8 @@ 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.
|
||||
@ -674,10 +684,13 @@ 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();
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -1442,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));
|
||||
@ -1454,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));
|
||||
@ -1732,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);
|
||||
@ -1746,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);
|
||||
@ -1770,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);
|
||||
@ -1778,6 +1790,7 @@ 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_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);
|
||||
@ -1787,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);
|
||||
@ -1794,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);
|
||||
|
||||
@ -1811,13 +1826,18 @@ FileDialog::FileDialog() {
|
||||
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);
|
||||
|
@ -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();
|
||||
@ -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();
|
||||
@ -2793,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");
|
||||
|
||||
@ -2813,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");
|
||||
@ -2869,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);
|
||||
|
||||
@ -2977,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));
|
||||
|
||||
@ -2985,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));
|
||||
|
||||
@ -2993,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));
|
||||
|
||||
@ -3005,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));
|
||||
@ -3014,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);
|
||||
@ -3026,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));
|
||||
|
||||
@ -3036,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);
|
||||
@ -3044,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);
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#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"
|
||||
@ -198,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;
|
||||
@ -269,6 +271,7 @@ private:
|
||||
float base_scale = 1.0;
|
||||
|
||||
Ref<StyleBox> panel;
|
||||
Ref<StyleBox> panel_focus;
|
||||
Color grid_major;
|
||||
Color grid_minor;
|
||||
|
||||
@ -303,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);
|
||||
|
||||
@ -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);
|
||||
|
@ -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"
|
||||
|
||||
@ -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;
|
||||
@ -285,6 +290,7 @@ void GraphNode::_resort() {
|
||||
valid_children_idx++;
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
port_pos_dirty = true;
|
||||
}
|
||||
@ -306,8 +312,308 @@ 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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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 +623,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 +636,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);
|
||||
|
||||
@ -356,6 +667,12 @@ void GraphNode::_notification(int p_what) {
|
||||
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.
|
||||
if (slot.draw_stylebox) {
|
||||
Control *child = Object::cast_to<Control>(get_child(E.key, false));
|
||||
@ -400,6 +717,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 +727,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 +756,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 +772,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 +844,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 +860,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;
|
||||
|
||||
@ -681,6 +1012,10 @@ void GraphNode::_port_pos_update() {
|
||||
|
||||
slot_index++;
|
||||
}
|
||||
slot_count = slot_index;
|
||||
if (selected_slot >= slot_count) {
|
||||
selected_slot = -1;
|
||||
}
|
||||
|
||||
port_pos_dirty = false;
|
||||
}
|
||||
@ -775,6 +1110,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;
|
||||
@ -884,9 +1238,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);
|
||||
@ -904,7 +1260,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);
|
||||
}
|
||||
|
@ -66,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;
|
||||
|
||||
@ -77,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;
|
||||
@ -112,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;
|
||||
|
||||
|
@ -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,6 +445,8 @@ 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();
|
||||
}
|
||||
|
||||
@ -434,9 +456,13 @@ void ItemList::deselect_all() {
|
||||
}
|
||||
|
||||
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;
|
||||
@ -565,6 +615,7 @@ void ItemList::set_max_text_lines(int p_lines) {
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
@ -52,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;
|
||||
@ -87,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;
|
||||
|
||||
@ -180,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);
|
||||
|
@ -100,6 +100,7 @@ void Label::set_uppercase(bool p_uppercase) {
|
||||
uppercase = p_uppercase;
|
||||
text_dirty = true;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -692,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) {
|
||||
@ -703,6 +713,7 @@ void Label::_notification(int p_what) {
|
||||
}
|
||||
text_dirty = true;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_configuration_warnings();
|
||||
} break;
|
||||
@ -749,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));
|
||||
@ -1070,7 +1085,7 @@ void Label::set_horizontal_alignment(HorizontalAlignment p_alignment) {
|
||||
}
|
||||
}
|
||||
horizontal_alignment = p_alignment;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -1103,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();
|
||||
@ -1193,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();
|
||||
}
|
||||
}
|
||||
@ -1285,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();
|
||||
}
|
||||
@ -1309,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();
|
||||
}
|
||||
@ -1326,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();
|
||||
@ -1448,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);
|
||||
|
||||
@ -1463,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);
|
||||
|
@ -90,6 +90,7 @@ private:
|
||||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> normal_style;
|
||||
Ref<StyleBox> focus_style;
|
||||
Ref<Font> font;
|
||||
|
||||
int font_size = 0;
|
||||
|
@ -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"
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -973,7 +976,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(INFINITY, INFINITY)) {
|
||||
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 +1014,7 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
|
||||
}
|
||||
text_changed_dirty = true;
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -1035,6 +1041,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 +1082,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();
|
||||
@ -1209,6 +1334,7 @@ void LineEdit::_notification(int p_what) {
|
||||
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);
|
||||
|
||||
@ -1921,6 +2047,8 @@ void LineEdit::set_caret_column(int p_column) {
|
||||
|
||||
caret_column = p_column;
|
||||
|
||||
queue_accessibility_update();
|
||||
|
||||
// Fit to window.
|
||||
|
||||
if (!is_inside_tree()) {
|
||||
@ -1989,6 +2117,7 @@ void LineEdit::set_caret_column(int p_column) {
|
||||
|
||||
scroll_offset = MIN(0, scroll_offset);
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -2083,6 +2212,7 @@ void LineEdit::deselect() {
|
||||
selection.enabled = false;
|
||||
selection.creating = false;
|
||||
selection.double_click = false;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -2141,6 +2271,7 @@ void LineEdit::selection_fill_at_caret() {
|
||||
}
|
||||
|
||||
selection.enabled = (selection.begin != selection.end);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void LineEdit::select_all() {
|
||||
@ -2156,6 +2287,7 @@ void LineEdit::select_all() {
|
||||
selection.begin = 0;
|
||||
selection.end = text.length();
|
||||
selection.enabled = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -2173,6 +2305,7 @@ void LineEdit::set_editable(bool p_editable) {
|
||||
_validate_caret_can_draw();
|
||||
|
||||
update_minimum_size();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -2242,6 +2375,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();
|
||||
}
|
||||
|
||||
@ -2635,6 +2769,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() {
|
||||
|
@ -105,6 +105,7 @@ private:
|
||||
Point2 ime_selection;
|
||||
|
||||
RID text_rid;
|
||||
RID accessibility_text_root_element;
|
||||
float full_width = 0.0;
|
||||
|
||||
bool selecting_enabled = true;
|
||||
@ -264,6 +265,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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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"))) {
|
||||
|
@ -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()));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ class Popup : public Window {
|
||||
GDCLASS(Popup, Window);
|
||||
|
||||
LocalVector<Window *> visible_parents;
|
||||
bool ac_popup = false;
|
||||
bool popped_up = false;
|
||||
|
||||
public:
|
||||
@ -59,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;
|
||||
|
@ -499,9 +499,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 +515,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 +541,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 +557,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 +746,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 +765,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 +777,7 @@ void PopupMenu::_mouse_over_update(const Point2 &p_over) {
|
||||
|
||||
if (over != mouse_over) {
|
||||
mouse_over = over;
|
||||
queue_accessibility_update();
|
||||
control->queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -1104,8 +1116,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 +1227,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);
|
||||
@ -1151,7 +1254,9 @@ void PopupMenu::_notification(int p_what) {
|
||||
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 +1271,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 +1387,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 +1449,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 +1473,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 +1497,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 +1523,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 +1547,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 +1573,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 +1599,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 +1637,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 +1667,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 +1697,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 +1729,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 +1759,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 +1791,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 +1818,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 +1832,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();
|
||||
@ -1733,13 +1856,16 @@ void PopupMenu::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);
|
||||
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 +1876,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,6 +1896,10 @@ 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();
|
||||
}
|
||||
}
|
||||
@ -1846,11 +1981,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();
|
||||
@ -1889,11 +2026,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();
|
||||
@ -1925,11 +2064,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();
|
||||
@ -1993,11 +2134,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();
|
||||
@ -2134,6 +2277,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();
|
||||
}
|
||||
|
||||
@ -2154,11 +2300,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();
|
||||
}
|
||||
@ -2175,11 +2323,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();
|
||||
}
|
||||
@ -2195,11 +2345,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();
|
||||
}
|
||||
@ -2299,11 +2451,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();
|
||||
}
|
||||
@ -2348,11 +2502,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();
|
||||
}
|
||||
@ -2386,11 +2542,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();
|
||||
}
|
||||
|
||||
@ -2412,6 +2569,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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2592,6 +2753,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);
|
||||
}
|
||||
@ -2611,6 +2776,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);
|
||||
@ -2626,7 +2792,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);
|
||||
}
|
||||
@ -2650,7 +2820,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();
|
||||
@ -3032,7 +3204,15 @@ 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();
|
||||
@ -3065,7 +3245,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);
|
||||
}
|
||||
|
@ -44,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);
|
||||
@ -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;
|
||||
@ -122,6 +126,7 @@ class PopupMenu : public Popup {
|
||||
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();
|
||||
|
||||
@ -249,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();
|
||||
|
@ -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()));
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -64,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)
|
||||
|
@ -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
@ -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,6 +195,8 @@ private:
|
||||
int line = 0;
|
||||
RID rid;
|
||||
|
||||
RID accessibility_item_element;
|
||||
|
||||
void _clear_children() {
|
||||
RichTextLabel *owner_rtl = ObjectDB::get_instance<RichTextLabel>(owner);
|
||||
while (subitems.size()) {
|
||||
@ -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;
|
||||
@ -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,6 +361,7 @@ private:
|
||||
|
||||
struct ItemTable : public Item {
|
||||
struct Column {
|
||||
String name;
|
||||
bool expand = false;
|
||||
bool shrink = true;
|
||||
int expand_ratio = 0;
|
||||
@ -354,6 +375,7 @@ private:
|
||||
LocalVector<float> rows;
|
||||
LocalVector<float> rows_no_padding;
|
||||
LocalVector<float> rows_baseline;
|
||||
String name;
|
||||
|
||||
int align_to_row = -1;
|
||||
int total_width = 0;
|
||||
@ -503,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);
|
||||
@ -552,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;
|
||||
|
||||
@ -580,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;
|
||||
@ -646,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 {
|
||||
@ -692,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);
|
||||
@ -720,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);
|
||||
@ -732,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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -353,8 +353,43 @@ 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:
|
||||
|
@ -99,6 +99,12 @@ 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;
|
||||
|
||||
|
@ -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: {
|
||||
|
@ -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;
|
||||
@ -650,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);
|
||||
|
||||
|
@ -34,10 +34,25 @@
|
||||
#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;
|
||||
|
||||
|
@ -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++) {
|
||||
|
@ -41,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;
|
||||
@ -49,6 +53,8 @@ private:
|
||||
|
||||
public:
|
||||
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
|
||||
|
||||
SplitContainerDragger();
|
||||
};
|
||||
|
||||
class SplitContainer : public Container {
|
||||
|
@ -351,6 +351,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: {
|
||||
@ -383,6 +412,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;
|
||||
@ -393,6 +462,7 @@ void TabBar::_notification(int p_what) {
|
||||
_shape(i);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
|
||||
@ -672,6 +742,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) {
|
||||
@ -702,6 +781,7 @@ void TabBar::set_tab_count(int p_count) {
|
||||
}
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
notify_property_list_changed();
|
||||
@ -737,6 +817,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);
|
||||
@ -785,6 +866,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();
|
||||
}
|
||||
|
||||
@ -811,6 +893,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();
|
||||
}
|
||||
@ -823,6 +906,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 {
|
||||
@ -836,7 +920,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();
|
||||
}
|
||||
}
|
||||
@ -851,12 +937,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();
|
||||
}
|
||||
@ -927,6 +1015,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();
|
||||
}
|
||||
@ -950,6 +1039,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();
|
||||
}
|
||||
@ -1117,6 +1207,8 @@ void TabBar::_update_cache(bool p_update_hover) {
|
||||
max_drawn_tab--;
|
||||
}
|
||||
}
|
||||
|
||||
tabs.write[i].accessibility_item_dirty = true;
|
||||
}
|
||||
|
||||
missing_right = max_drawn_tab < tabs.size() - 1;
|
||||
@ -1171,6 +1263,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();
|
||||
|
||||
@ -1189,12 +1282,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();
|
||||
@ -1202,6 +1302,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;
|
||||
@ -1251,6 +1356,7 @@ void TabBar::remove_tab(int p_idx) {
|
||||
}
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
update_minimum_size();
|
||||
notify_property_list_changed();
|
||||
@ -1284,7 +1390,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(INFINITY, INFINITY)) ? current : get_tab_idx_at_point(p_point);
|
||||
if (tab_over < 0) {
|
||||
return Variant();
|
||||
}
|
||||
@ -1349,7 +1455,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_closest_tab_idx_to_point(p_point);
|
||||
int hover_now = (p_point == Vector2(INFINITY, INFINITY)) ? current : get_closest_tab_idx_to_point(p_point);
|
||||
NodePath from_path = d["from_path"];
|
||||
NodePath to_path = get_path();
|
||||
|
||||
@ -1407,6 +1513,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);
|
||||
|
||||
@ -1426,6 +1534,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();
|
||||
}
|
||||
|
||||
@ -1515,6 +1624,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);
|
||||
|
||||
@ -1539,6 +1650,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();
|
||||
}
|
||||
@ -1957,6 +2069,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));
|
||||
|
@ -54,6 +54,9 @@ public:
|
||||
|
||||
private:
|
||||
struct Tab {
|
||||
mutable RID accessibility_item_element;
|
||||
mutable bool accessibility_item_dirty = true;
|
||||
|
||||
String text;
|
||||
String tooltip;
|
||||
|
||||
@ -170,6 +173,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 +194,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);
|
||||
|
@ -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) {
|
||||
@ -556,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));
|
||||
@ -579,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;
|
||||
}
|
||||
@ -607,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");
|
||||
|
@ -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;
|
||||
|
@ -224,9 +224,35 @@ _FORCE_INLINE_ const String &TextEdit::Text::get_text_with_ime(int p_line) const
|
||||
}
|
||||
}
|
||||
|
||||
const Vector<RID> TextEdit::Text::get_accessibility_elements(int p_line) {
|
||||
ERR_FAIL_INDEX_V(p_line, text.size(), Vector<RID>());
|
||||
|
||||
return text[p_line].accessibility_text_root_element;
|
||||
}
|
||||
|
||||
void TextEdit::Text::update_accessibility(int p_line, RID p_root) {
|
||||
ERR_FAIL_INDEX(p_line, text.size());
|
||||
|
||||
Line &l = text.write[p_line];
|
||||
if (l.accessibility_text_root_element.is_empty()) {
|
||||
for (int i = 0; i < l.data_buf->get_line_count(); i++) {
|
||||
RID rid = DisplayServer::get_singleton()->accessibility_create_sub_text_edit_elements(p_root, l.data_buf->get_line_rid(i), max_line_height, p_line);
|
||||
l.accessibility_text_root_element.push_back(rid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextEdit::Text::invalidate_cache(int p_line, bool p_text_changed) {
|
||||
ERR_FAIL_INDEX(p_line, text.size());
|
||||
|
||||
Line &l = text.write[p_line];
|
||||
for (const RID rid : l.accessibility_text_root_element) {
|
||||
if (rid.is_valid()) {
|
||||
DisplayServer::get_singleton()->accessibility_free_element(rid);
|
||||
}
|
||||
}
|
||||
l.accessibility_text_root_element.clear();
|
||||
|
||||
if (font.is_null()) {
|
||||
return; // Not in tree?
|
||||
}
|
||||
@ -564,8 +590,173 @@ String TextEdit::Text::get_enabled_word_separators() const {
|
||||
/// TEXT EDIT ///
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TextEdit::_accessibility_action_set_selection(const Variant &p_data) {
|
||||
Dictionary new_selection = p_data;
|
||||
RID sel_start = new_selection["start_element"];
|
||||
Vector2i sel_start_line = DisplayServer::get_singleton()->accessibility_element_get_meta(sel_start);
|
||||
int sel_start_pos = new_selection["start_char"];
|
||||
|
||||
RID sel_end = new_selection["end_element"];
|
||||
Vector2i sel_end_line = DisplayServer::get_singleton()->accessibility_element_get_meta(sel_end);
|
||||
int sel_end_pos = new_selection["end_char"];
|
||||
|
||||
remove_secondary_carets();
|
||||
select(sel_start_line.x, sel_start_pos, sel_end_line.x, sel_end_pos, 0);
|
||||
}
|
||||
|
||||
void TextEdit::_accessibility_action_replace_selected(const Variant &p_data) {
|
||||
String new_text = p_data;
|
||||
insert_text_at_caret(new_text);
|
||||
}
|
||||
|
||||
void TextEdit::_accessibility_action_set_value(const Variant &p_data) {
|
||||
String new_text = p_data;
|
||||
set_text(new_text);
|
||||
}
|
||||
|
||||
void TextEdit::_accessibility_action_menu(const Variant &p_data) {
|
||||
if (context_menu_enabled) {
|
||||
_update_context_menu();
|
||||
adjust_viewport_to_caret();
|
||||
menu->set_position(get_screen_position() + get_caret_draw_pos());
|
||||
menu->reset_size();
|
||||
menu->popup();
|
||||
menu->grab_focus();
|
||||
}
|
||||
}
|
||||
|
||||
void TextEdit::_accessibility_scroll_down(const Variant &p_data) {
|
||||
v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() / 4);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void TextEdit::_accessibility_scroll_left(const Variant &p_data) {
|
||||
h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() / 4);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void TextEdit::_accessibility_scroll_right(const Variant &p_data) {
|
||||
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() / 4);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void TextEdit::_accessibility_scroll_up(const Variant &p_data) {
|
||||
v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() / 4);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void TextEdit::_accessibility_scroll_set(const Variant &p_data) {
|
||||
const Point2 &pos = p_data;
|
||||
h_scroll->set_value(pos.x);
|
||||
v_scroll->set_value(pos.y);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void TextEdit::_accessibility_action_scroll_into_view(const Variant &p_data, int p_line, int p_wrap) {
|
||||
double delta = get_scroll_pos_for_line(p_line, p_wrap) - get_v_scroll();
|
||||
if (delta < 0) {
|
||||
_scroll_up(-delta, false);
|
||||
} else {
|
||||
_scroll_down(delta, false);
|
||||
}
|
||||
}
|
||||
|
||||
void TextEdit::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_EXIT_TREE:
|
||||
case NOTIFICATION_ACCESSIBILITY_INVALIDATE: {
|
||||
text.clear_accessibility();
|
||||
accessibility_text_root_element_nl = 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_MULTILINE_TEXT_FIELD);
|
||||
if (text.size() == 1 && text[0].is_empty()) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_placeholder(ae, atr(placeholder_text));
|
||||
}
|
||||
if (!placeholder_text.is_empty() && get_accessibility_name().is_empty()) {
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(ae, atr(placeholder_text));
|
||||
}
|
||||
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, &TextEdit::_accessibility_action_set_selection));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_REPLACE_SELECTED_TEXT, callable_mp(this, &TextEdit::_accessibility_action_replace_selected));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SET_VALUE, callable_mp(this, &TextEdit::_accessibility_action_set_value));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SHOW_CONTEXT_MENU, callable_mp(this, &TextEdit::_accessibility_action_menu));
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_DOWN, callable_mp(this, &TextEdit::_accessibility_scroll_down));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_LEFT, callable_mp(this, &TextEdit::_accessibility_scroll_left));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_RIGHT, callable_mp(this, &TextEdit::_accessibility_scroll_right));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SCROLL_UP, callable_mp(this, &TextEdit::_accessibility_scroll_up));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SET_SCROLL_OFFSET, callable_mp(this, &TextEdit::_accessibility_scroll_set));
|
||||
|
||||
int first_vis_line = get_first_visible_line();
|
||||
int row_height = get_line_height();
|
||||
int xmargin_beg = theme_cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding;
|
||||
Size2 size = get_size();
|
||||
bool rtl = is_layout_rtl();
|
||||
int lines_drawn = 0;
|
||||
|
||||
RID selection_start;
|
||||
RID selection_end;
|
||||
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
text.update_accessibility(i, ae);
|
||||
const Ref<TextParagraph> &ac_buf = text.get_line_data(i);
|
||||
const Vector<RID> &text_aes = text.get_accessibility_elements(i);
|
||||
for (int j = 0; j < text_aes.size(); j++) {
|
||||
float text_off_x = 0.0;
|
||||
float text_off_y = 0.0;
|
||||
if (!editable) {
|
||||
text_off_x = theme_cache.style_readonly->get_offset().x / 2;
|
||||
text_off_x -= theme_cache.style_normal->get_offset().x / 2;
|
||||
text_off_y = theme_cache.style_readonly->get_offset().y / 2;
|
||||
} else {
|
||||
text_off_y = theme_cache.style_normal->get_offset().y / 2;
|
||||
}
|
||||
|
||||
text_off_y += (lines_drawn + j) * row_height + theme_cache.line_spacing / 2;
|
||||
text_off_y -= (first_vis_line + first_visible_line_wrap_ofs) * row_height;
|
||||
text_off_y -= _get_v_scroll_offset() * row_height;
|
||||
|
||||
int char_margin = xmargin_beg - first_visible_col;
|
||||
if (rtl) {
|
||||
char_margin = size.width - char_margin - ac_buf->get_line_width(j);
|
||||
}
|
||||
|
||||
DisplayServer::get_singleton()->accessibility_update_set_flag(text_aes[j], DisplayServer::AccessibilityFlags::FLAG_HIDDEN, _is_line_hidden(i));
|
||||
Transform2D tr;
|
||||
tr.set_origin(Point2(char_margin + text_off_x, text_off_y));
|
||||
DisplayServer::get_singleton()->accessibility_update_set_transform(text_aes[j], tr);
|
||||
DisplayServer::get_singleton()->accessibility_update_set_name(text_aes[j], vformat(RTR("Line %d"), i));
|
||||
DisplayServer::get_singleton()->accessibility_element_set_meta(text_aes[j], Vector2i(i, j));
|
||||
DisplayServer::get_singleton()->accessibility_update_add_action(text_aes[j], DisplayServer::AccessibilityAction::ACTION_SCROLL_INTO_VIEW, callable_mp(this, &TextEdit::_accessibility_action_scroll_into_view).bind(i, j));
|
||||
}
|
||||
lines_drawn += ac_buf->get_line_count();
|
||||
}
|
||||
if (accessibility_text_root_element_nl.is_null()) {
|
||||
accessibility_text_root_element_nl = DisplayServer::get_singleton()->accessibility_create_sub_text_edit_elements(ae, RID(), get_line_height());
|
||||
}
|
||||
|
||||
// Selection.
|
||||
if (carets.size() > 0) {
|
||||
if (carets[0].selection.active) {
|
||||
int start_wrap = get_line_wrap_index_at_column(carets[0].selection.origin_line, carets[0].selection.origin_column);
|
||||
RID start_rid = text.get_accessibility_elements(carets[0].selection.origin_line)[start_wrap];
|
||||
|
||||
int end_wrap = get_line_wrap_index_at_column(carets[0].line, carets[0].column);
|
||||
RID end_rid = text.get_accessibility_elements(carets[0].line)[end_wrap];
|
||||
DisplayServer::get_singleton()->accessibility_update_set_text_selection(ae, start_rid, carets[0].selection.origin_column, end_rid, carets[0].column);
|
||||
} else {
|
||||
int caret_wrap = get_line_wrap_index_at_column(carets[0].line, carets[0].column);
|
||||
RID caret_rid = text.get_accessibility_elements(carets[0].line)[caret_wrap];
|
||||
DisplayServer::get_singleton()->accessibility_update_set_text_selection(ae, caret_rid, carets[0].column, caret_rid, carets[0].column);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_POSTINITIALIZE: {
|
||||
_update_caches();
|
||||
} break;
|
||||
@ -1694,6 +1885,8 @@ void TextEdit::_notification(int p_what) {
|
||||
|
||||
_update_ime_text();
|
||||
adjust_viewport_to_caret(0);
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
} break;
|
||||
@ -1864,6 +2057,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
if (mb->get_button_index() == MouseButton::WHEEL_UP && !mb->is_command_or_control_pressed()) {
|
||||
if (mb->is_shift_pressed()) {
|
||||
h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
|
||||
queue_accessibility_update();
|
||||
} else if (mb->is_alt_pressed()) {
|
||||
// Scroll 5 times as fast as normal (like in Visual Studio Code).
|
||||
_scroll_up(15 * mb->get_factor(), true);
|
||||
@ -1875,6 +2069,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
if (mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_or_control_pressed()) {
|
||||
if (mb->is_shift_pressed()) {
|
||||
h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor()));
|
||||
queue_accessibility_update();
|
||||
} else if (mb->is_alt_pressed()) {
|
||||
// Scroll 5 times as fast as normal (like in Visual Studio Code).
|
||||
_scroll_down(15 * mb->get_factor(), true);
|
||||
@ -1885,9 +2080,11 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
}
|
||||
if (mb->get_button_index() == MouseButton::WHEEL_LEFT) {
|
||||
h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor()));
|
||||
queue_accessibility_update();
|
||||
}
|
||||
if (mb->get_button_index() == MouseButton::WHEEL_RIGHT) {
|
||||
h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor()));
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
if (mb->get_button_index() == MouseButton::LEFT) {
|
||||
@ -1961,6 +2158,8 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
return;
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
|
||||
last_dblclk = 0;
|
||||
} else if (!mb->is_shift_pressed()) {
|
||||
if (drag_and_drop_selection_enabled && mouse_over_selection_caret >= 0) {
|
||||
@ -2005,6 +2204,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
last_dblclk = OS::get_singleton()->get_ticks_msec();
|
||||
last_dblclk_pos = mb->get_position();
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -2086,6 +2286,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) {
|
||||
accept_event(); // Accept event if scroll changed.
|
||||
}
|
||||
queue_accessibility_update();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -2417,8 +2618,15 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle Tab mode.
|
||||
if (k->is_action("ui_focus_mode", true)) {
|
||||
tab_input_mode = !tab_input_mode;
|
||||
accept_event();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle tab as it has no set unicode value.
|
||||
if (k->is_action("ui_text_indent", true)) {
|
||||
if (tab_input_mode && k->is_action("ui_text_indent", true)) {
|
||||
if (editable) {
|
||||
insert_text_at_caret("\t");
|
||||
}
|
||||
@ -2996,6 +3204,7 @@ void TextEdit::_update_caches() {
|
||||
syntax_highlighter->set_text_edit(this);
|
||||
}
|
||||
_clear_syntax_highlighting_cache();
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void TextEdit::_close_ime_window() {
|
||||
@ -3039,6 +3248,7 @@ void TextEdit::_update_ime_text() {
|
||||
}
|
||||
}
|
||||
_clear_syntax_highlighting_cache();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -3111,7 +3321,9 @@ bool TextEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const
|
||||
void TextEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
|
||||
Control::drop_data(p_point, p_data);
|
||||
|
||||
if (p_data.is_string() && is_editable()) {
|
||||
if (p_point == Vector2(INFINITY, INFINITY)) {
|
||||
insert_text_at_caret(p_data);
|
||||
} else if (p_data.is_string() && is_editable()) {
|
||||
Point2i pos = get_line_column_at_pos(get_local_mouse_pos());
|
||||
int drop_at_line = pos.y;
|
||||
int drop_at_column = pos.x;
|
||||
@ -3215,6 +3427,7 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const {
|
||||
|
||||
void TextEdit::set_tooltip_request_func(const Callable &p_tooltip_callback) {
|
||||
tooltip_callback = p_tooltip_callback;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
/* Text */
|
||||
@ -3261,7 +3474,7 @@ void TextEdit::set_editable(bool p_editable) {
|
||||
}
|
||||
|
||||
editable = p_editable;
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -3292,6 +3505,7 @@ void TextEdit::set_text_direction(Control::TextDirection p_text_direction) {
|
||||
menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR);
|
||||
menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -3312,6 +3526,7 @@ void TextEdit::set_language(const String &p_language) {
|
||||
text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
|
||||
text.invalidate_all();
|
||||
_update_placeholder();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -3326,6 +3541,7 @@ void TextEdit::set_structured_text_bidi_override(TextServer::StructuredTextParse
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i]));
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -3343,6 +3559,7 @@ void TextEdit::set_structured_text_bidi_override_options(Array p_args) {
|
||||
for (int i = 0; i < text.size(); i++) {
|
||||
text.set(i, text[i], structured_text_parser(st_parser, st_args, text[i]));
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -3358,6 +3575,7 @@ void TextEdit::set_tab_size(const int p_size) {
|
||||
text.set_tab_size(p_size);
|
||||
text.invalidate_all_lines();
|
||||
_update_placeholder();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -3379,6 +3597,14 @@ bool TextEdit::is_indent_wrapped_lines() const {
|
||||
return text.is_indent_wrapped_lines();
|
||||
}
|
||||
|
||||
void TextEdit::set_tab_input_mode(bool p_enabled) {
|
||||
tab_input_mode = p_enabled;
|
||||
}
|
||||
|
||||
bool TextEdit::get_tab_input_mode() const {
|
||||
return tab_input_mode;
|
||||
}
|
||||
|
||||
// User controls
|
||||
void TextEdit::set_overtype_mode_enabled(bool p_enabled) {
|
||||
if (overtype_mode == p_enabled) {
|
||||
@ -3508,7 +3734,7 @@ void TextEdit::set_text(const String &p_text) {
|
||||
|
||||
set_caret_line(0);
|
||||
set_caret_column(0);
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
setting_text = false;
|
||||
emit_signal(SNAME("text_set"));
|
||||
@ -3537,6 +3763,7 @@ void TextEdit::set_placeholder(const String &p_text) {
|
||||
|
||||
placeholder_text = p_text;
|
||||
_update_placeholder();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -3773,6 +4000,9 @@ void TextEdit::remove_line_at(int p_line, bool p_move_carets_down) {
|
||||
_offset_carets_after(next_line, next_column, from_line, from_column);
|
||||
end_multicaret_edit();
|
||||
end_complex_operation();
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void TextEdit::insert_text_at_caret(const String &p_text, int p_caret) {
|
||||
@ -4153,6 +4383,7 @@ void TextEdit::start_action(EditAction p_action) {
|
||||
void TextEdit::end_action() {
|
||||
if (current_action != EditAction::ACTION_NONE) {
|
||||
pending_action_end = true;
|
||||
queue_accessibility_update();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4172,6 +4403,8 @@ void TextEdit::begin_complex_operation() {
|
||||
void TextEdit::end_complex_operation() {
|
||||
_push_current_op();
|
||||
|
||||
queue_accessibility_update();
|
||||
|
||||
complex_operation_count = MAX(complex_operation_count - 1, 0);
|
||||
if (complex_operation_count > 0) {
|
||||
return;
|
||||
@ -4264,6 +4497,7 @@ void TextEdit::undo() {
|
||||
_selection_changed();
|
||||
}
|
||||
adjust_viewport_to_caret();
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void TextEdit::redo() {
|
||||
@ -4320,6 +4554,7 @@ void TextEdit::redo() {
|
||||
_selection_changed();
|
||||
}
|
||||
adjust_viewport_to_caret();
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
void TextEdit::clear_undo_history() {
|
||||
@ -4591,7 +4826,7 @@ Rect2i TextEdit::get_rect_at_line_column(int p_line, int p_column) const {
|
||||
ERR_FAIL_COND_V(p_column < 0, Rect2i(-1, -1, 0, 0));
|
||||
ERR_FAIL_COND_V(p_column > text[p_line].length(), Rect2i(-1, -1, 0, 0));
|
||||
|
||||
if (text.size() == 1 && text[0].length() == 0) {
|
||||
if (text.size() == 1 && text[0].is_empty()) {
|
||||
// The TextEdit is empty.
|
||||
return Rect2i();
|
||||
}
|
||||
@ -4841,6 +5076,7 @@ void TextEdit::remove_secondary_carets() {
|
||||
if (drag_caret_index >= 0) {
|
||||
drag_caret_index = -1;
|
||||
}
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
int TextEdit::get_caret_count() const {
|
||||
@ -5262,6 +5498,7 @@ void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_
|
||||
if (caret_moved) {
|
||||
_caret_changed(p_caret);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
int TextEdit::get_caret_line(int p_caret) const {
|
||||
@ -5296,6 +5533,7 @@ void TextEdit::set_caret_column(int p_column, bool p_adjust_viewport, int p_care
|
||||
if (caret_moved) {
|
||||
_caret_changed(p_caret);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
int TextEdit::get_caret_column(int p_caret) const {
|
||||
@ -5384,7 +5622,7 @@ void TextEdit::select_all() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (text.size() == 1 && text[0].length() == 0) {
|
||||
if (text.size() == 1 && text[0].is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5401,7 +5639,7 @@ void TextEdit::select_word_under_caret(int p_caret) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (text.size() == 1 && text[0].length() == 0) {
|
||||
if (text.size() == 1 && text[0].is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5446,7 +5684,7 @@ void TextEdit::add_selection_for_next_occurrence() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (text.size() == 1 && text[0].length() == 0) {
|
||||
if (text.size() == 1 && text[0].is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5489,7 +5727,7 @@ void TextEdit::skip_selection_for_next_occurrence() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (text.size() == 1 && text[0].length() == 0) {
|
||||
if (text.size() == 1 && text[0].is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5558,6 +5796,9 @@ void TextEdit::select(int p_origin_line, int p_origin_column, int p_caret_line,
|
||||
if (had_selection != activate) {
|
||||
_selection_changed(p_caret);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
bool TextEdit::has_selection(int p_caret) const {
|
||||
@ -5802,6 +6043,9 @@ void TextEdit::deselect(int p_caret) {
|
||||
if (selection_changed) {
|
||||
_selection_changed(p_caret);
|
||||
}
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void TextEdit::delete_selection(int p_caret) {
|
||||
@ -5843,6 +6087,7 @@ void TextEdit::set_line_wrapping_mode(LineWrappingMode p_wrapping_mode) {
|
||||
if (line_wrapping_mode != p_wrapping_mode) {
|
||||
line_wrapping_mode = p_wrapping_mode;
|
||||
_update_wrap_at_column(true);
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -5859,6 +6104,7 @@ void TextEdit::set_autowrap_mode(TextServer::AutowrapMode p_mode) {
|
||||
autowrap_mode = p_mode;
|
||||
if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE) {
|
||||
_update_wrap_at_column(true);
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -5964,6 +6210,7 @@ void TextEdit::set_v_scroll(double p_scroll) {
|
||||
if (p_scroll >= max_v_scroll - 1.0) {
|
||||
_scroll_moved(v_scroll->get_value());
|
||||
}
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
double TextEdit::get_v_scroll() const {
|
||||
@ -5975,6 +6222,7 @@ void TextEdit::set_h_scroll(int p_scroll) {
|
||||
p_scroll = 0;
|
||||
}
|
||||
h_scroll->set_value(p_scroll);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
int TextEdit::get_h_scroll() const {
|
||||
@ -6550,6 +6798,7 @@ void TextEdit::set_draw_control_chars(bool p_enabled) {
|
||||
text.set_draw_control_chars(draw_control_chars);
|
||||
text.invalidate_font();
|
||||
_update_placeholder();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
@ -6615,6 +6864,9 @@ void TextEdit::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_indent_wrapped_lines", "enabled"), &TextEdit::set_indent_wrapped_lines);
|
||||
ClassDB::bind_method(D_METHOD("is_indent_wrapped_lines"), &TextEdit::is_indent_wrapped_lines);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_tab_input_mode", "enabled"), &TextEdit::set_tab_input_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_tab_input_mode"), &TextEdit::get_tab_input_mode);
|
||||
|
||||
// User controls
|
||||
ClassDB::bind_method(D_METHOD("set_overtype_mode_enabled", "enabled"), &TextEdit::set_overtype_mode_enabled);
|
||||
ClassDB::bind_method(D_METHOD("is_overtype_mode_enabled"), &TextEdit::is_overtype_mode_enabled);
|
||||
@ -7037,6 +7289,7 @@ void TextEdit::_bind_methods() {
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Arbitrary:1,Word:2,Word (Smart):3"), "set_autowrap_mode", "get_autowrap_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indent_wrapped_lines"), "set_indent_wrapped_lines", "is_indent_wrapped_lines");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tab_input_mode"), "set_tab_input_mode", "get_tab_input_mode");
|
||||
|
||||
ADD_GROUP("Scroll", "scroll_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_smooth"), "set_smooth_scroll_enabled", "is_smooth_scroll_enabled");
|
||||
@ -7147,6 +7400,7 @@ void TextEdit::_set_hiding_enabled(bool p_enabled) {
|
||||
_unhide_all_lines();
|
||||
}
|
||||
hiding_enabled = p_enabled;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -7164,6 +7418,7 @@ void TextEdit::_unhide_all_lines() {
|
||||
text.set_hidden(i, false);
|
||||
}
|
||||
_update_scrollbars();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -7181,6 +7436,7 @@ void TextEdit::_set_line_as_hidden(int p_line, bool p_hidden) {
|
||||
if (_is_hiding_enabled() || !p_hidden) {
|
||||
text.set_hidden(p_line, p_hidden);
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -7833,6 +8089,7 @@ void TextEdit::_selection_changed(int p_caret) {
|
||||
}
|
||||
|
||||
_cancel_drag_and_drop_text();
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -8035,6 +8292,7 @@ void TextEdit::_update_wrap_at_column(bool p_force) {
|
||||
first_visible_line_wrap_ofs = 0;
|
||||
}
|
||||
set_line_as_first_visible(first_visible_line, first_visible_line_wrap_ofs);
|
||||
queue_accessibility_update();
|
||||
}
|
||||
|
||||
/* Viewport. */
|
||||
@ -8151,6 +8409,7 @@ void TextEdit::_scroll_moved(double p_to_val) {
|
||||
first_visible_line = n_line;
|
||||
first_visible_line_wrap_ofs = wi;
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -8185,6 +8444,7 @@ void TextEdit::_scroll_up(real_t p_delta, bool p_animate) {
|
||||
}
|
||||
if (!p_animate || Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) {
|
||||
v_scroll->set_value(target_v_scroll);
|
||||
queue_accessibility_update();
|
||||
} else {
|
||||
scrolling = true;
|
||||
set_physics_process_internal(true);
|
||||
@ -8213,6 +8473,7 @@ void TextEdit::_scroll_down(real_t p_delta, bool p_animate) {
|
||||
}
|
||||
if (!p_animate || Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) {
|
||||
v_scroll->set_value(target_v_scroll);
|
||||
queue_accessibility_update();
|
||||
} else {
|
||||
scrolling = true;
|
||||
set_physics_process_internal(true);
|
||||
@ -8326,6 +8587,8 @@ void TextEdit::_adjust_viewport_to_caret_horizontally(int p_caret, bool p_maximi
|
||||
}
|
||||
|
||||
h_scroll->set_value(first_visible_col);
|
||||
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
@ -8418,6 +8681,7 @@ void TextEdit::_update_gutter_width() {
|
||||
if (get_viewport()) {
|
||||
hovered_gutter = _get_hovered_gutter(get_local_mouse_position());
|
||||
}
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
|
@ -144,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;
|
||||
@ -227,6 +230,14 @@ 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;
|
||||
@ -275,13 +286,14 @@ private:
|
||||
|
||||
/* Text */
|
||||
Text text;
|
||||
|
||||
bool setting_text = false;
|
||||
|
||||
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;
|
||||
@ -628,6 +640,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();
|
||||
@ -718,6 +732,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)
|
||||
@ -765,6 +790,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;
|
||||
|
@ -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) {
|
||||
|
41
scene/gui/tree.compat.inc
Normal file
41
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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user