diff options
64 files changed, 803 insertions, 584 deletions
diff --git a/core/debugger/remote_debugger_peer.cpp b/core/debugger/remote_debugger_peer.cpp index a45430465f..8c6e95e276 100644 --- a/core/debugger/remote_debugger_peer.cpp +++ b/core/debugger/remote_debugger_peer.cpp @@ -68,8 +68,8 @@ void RemoteDebuggerPeerTCP::close() { running = false; thread.wait_to_finish(); tcp_client->disconnect_from_host(); - out_buf.resize(0); - in_buf.resize(0); + out_buf.clear(); + in_buf.clear(); } RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) { @@ -190,7 +190,8 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po } void RemoteDebuggerPeerTCP::_thread_func(void *p_ud) { - const uint64_t min_tick = 100; + // Update in time for 144hz monitors + const uint64_t min_tick = 6900; RemoteDebuggerPeerTCP *peer = (RemoteDebuggerPeerTCP *)p_ud; while (peer->running && peer->is_peer_connected()) { uint64_t ticks_usec = OS::get_singleton()->get_ticks_usec(); diff --git a/core/debugger/script_debugger.cpp b/core/debugger/script_debugger.cpp index 36723e5568..4dd93249ef 100644 --- a/core/debugger/script_debugger.cpp +++ b/core/debugger/script_debugger.cpp @@ -104,7 +104,7 @@ void ScriptDebugger::send_error(const String &p_func, const String &p_file, int // Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way. error_stack_info.append_array(p_stack_info); EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type); - error_stack_info.resize(0); + error_stack_info.clear(); } Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const { diff --git a/core/io/compression.cpp b/core/io/compression.cpp index d1f915f064..ae5ccf8354 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -212,7 +212,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s strm.avail_in = p_src_size; // Ensure the destination buffer is empty - p_dst_vect->resize(0); + p_dst_vect->clear(); // decompress until deflate stream ends or end of file do { @@ -244,7 +244,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s WARN_PRINT(strm.msg); } (void)inflateEnd(&strm); - p_dst_vect->resize(0); + p_dst_vect->clear(); return ret; } } while (strm.avail_out > 0 && strm.avail_in > 0); @@ -254,7 +254,7 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s // Enforce max output size if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { (void)inflateEnd(&strm); - p_dst_vect->resize(0); + p_dst_vect->clear(); return Z_BUF_ERROR; } } while (ret != Z_STREAM_END); diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index e90d1695e5..0af236f766 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -39,7 +39,7 @@ void PacketPeer::set_encode_buffer_max_size(int p_max_size) { ERR_FAIL_COND_MSG(p_max_size < 1024, "Max encode buffer must be at least 1024 bytes"); ERR_FAIL_COND_MSG(p_max_size > 256 * 1024 * 1024, "Max encode buffer cannot exceed 256 MiB"); encode_buffer_max_size = next_power_of_2(p_max_size); - encode_buffer.resize(0); + encode_buffer.clear(); } int PacketPeer::get_encode_buffer_max_size() const { diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp index 28ebe811c9..c65968ef03 100644 --- a/core/io/stream_peer.cpp +++ b/core/io/stream_peer.cpp @@ -98,7 +98,7 @@ Array StreamPeer::_get_partial_data(int p_bytes) { Error err = get_partial_data(&w[0], p_bytes, received); if (err != OK) { - data.resize(0); + data.clear(); } else if (received != data.size()) { data.resize(received); } @@ -563,7 +563,7 @@ Vector<uint8_t> StreamPeerBuffer::get_data_array() const { } void StreamPeerBuffer::clear() { - data.resize(0); + data.clear(); pointer = 0; } diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 912ffb8b16..bd292f4c2a 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -2129,7 +2129,7 @@ bool ConvexHullInternal::shift_face(Face *p_face, real_t p_amount, LocalVector<V printf("Needed %d iterations to remove part\n", n); #endif - p_stack.resize(0); + p_stack.clear(); p_face->origin = shifted_origin; return true; @@ -2167,9 +2167,9 @@ real_t ConvexHullComputer::compute(const Vector3 *p_coords, int32_t p_count, rea return shift; } - vertices.resize(0); - edges.resize(0); - faces.resize(0); + vertices.clear(); + edges.clear(); + faces.clear(); LocalVector<ConvexHullInternal::Vertex *> old_vertices; get_vertex_copy(hull.vertex_list, old_vertices); diff --git a/core/templates/vector.h b/core/templates/vector.h index bd4c6ade86..0877e04e01 100644 --- a/core/templates/vector.h +++ b/core/templates/vector.h @@ -95,9 +95,7 @@ public: void append_array(Vector<T> p_other); - bool has(const T &p_val) const { - return find(p_val, 0) != -1; - } + _FORCE_INLINE_ bool has(const T &p_val) const { return find(p_val) != -1; } template <class C> void sort_custom() { diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index 65d1654c21..6285b1d155 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -93,7 +93,7 @@ <method name="get_area" qualifiers="const"> <return type="float" /> <description> - Returns the area of the [Rect2]. + Returns the area of the [Rect2]. See also [method has_no_area]. </description> </method> <method name="get_center" qualifiers="const"> @@ -130,7 +130,8 @@ <method name="has_no_area" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if the [Rect2] is flat or empty. + Returns [code]true[/code] if the [Rect2] is flat or empty, [code]false[/code] otherwise. See also [method get_area]. + [b]Note:[/b] If the [Rect2] has a negative size and is not flat or empty, [method has_no_area] will return [code]true[/code]. </description> </method> <method name="has_point" qualifiers="const"> diff --git a/doc/classes/Rect2i.xml b/doc/classes/Rect2i.xml index e070824297..a542bad82b 100644 --- a/doc/classes/Rect2i.xml +++ b/doc/classes/Rect2i.xml @@ -90,7 +90,7 @@ <method name="get_area" qualifiers="const"> <return type="int" /> <description> - Returns the area of the [Rect2i]. + Returns the area of the [Rect2i]. See also [method has_no_area]. </description> </method> <method name="get_center" qualifiers="const"> @@ -128,7 +128,8 @@ <method name="has_no_area" qualifiers="const"> <return type="bool" /> <description> - Returns [code]true[/code] if the [Rect2i] is flat or empty. + Returns [code]true[/code] if the [Rect2i] is flat or empty, [code]false[/code] otherwise. See also [method get_area]. + [b]Note:[/b] If the [Rect2i] has a negative size and is not flat or empty, [method has_no_area] will return [code]true[/code]. </description> </method> <method name="has_point" qualifiers="const"> diff --git a/doc/classes/TextLine.xml b/doc/classes/TextLine.xml index bf1abf86ae..7bfc7eed7e 100644 --- a/doc/classes/TextLine.xml +++ b/doc/classes/TextLine.xml @@ -26,6 +26,7 @@ <argument index="2" name="size" type="int" /> <argument index="3" name="opentype_features" type="Dictionary" default="{}" /> <argument index="4" name="language" type="String" default="""" /> + <argument index="5" name="meta" type="Variant" default="null" /> <description> Adds text span and font to draw it. </description> diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml index 3d3c0dd6ff..acf3dcd43b 100644 --- a/doc/classes/TextParagraph.xml +++ b/doc/classes/TextParagraph.xml @@ -26,6 +26,7 @@ <argument index="2" name="size" type="int" /> <argument index="3" name="opentype_features" type="Dictionary" default="{}" /> <argument index="4" name="language" type="String" default="""" /> + <argument index="5" name="meta" type="Variant" default="null" /> <description> Adds text span and font to draw it. </description> diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 61024ef0c8..9025e4ff2a 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -896,6 +896,32 @@ [b]Note:[/b] This function is used by during project export, to include TextServer database. </description> </method> + <method name="shaped_get_span_count" qualifiers="const"> + <return type="int" /> + <argument index="0" name="shaped" type="RID" /> + <description> + Returns number of text spans added using [method shaped_text_add_string] or [method shaped_text_add_object]. + </description> + </method> + <method name="shaped_get_span_meta" qualifiers="const"> + <return type="Variant" /> + <argument index="0" name="shaped" type="RID" /> + <argument index="1" name="index" type="int" /> + <description> + Returns text span metadata. + </description> + </method> + <method name="shaped_set_span_update_font"> + <return type="void" /> + <argument index="0" name="shaped" type="RID" /> + <argument index="1" name="index" type="int" /> + <argument index="2" name="fonts" type="Array" /> + <argument index="3" name="size" type="int" /> + <argument index="4" name="opentype_features" type="Dictionary" default="{}" /> + <description> + Changes text span font, font size and OpenType features, without changing the text. + </description> + </method> <method name="shaped_text_add_object"> <return type="bool" /> <argument index="0" name="shaped" type="RID" /> @@ -915,6 +941,7 @@ <argument index="3" name="size" type="int" /> <argument index="4" name="opentype_features" type="Dictionary" default="{}" /> <argument index="5" name="language" type="String" default="""" /> + <argument index="6" name="meta" type="Variant" default="null" /> <description> Adds text span and font to draw it to the text buffer. </description> diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 8b4825026d..5967340f8c 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -904,6 +904,32 @@ [b]Note:[/b] This function is used by during project export, to include TextServer database. </description> </method> + <method name="_shaped_get_span_count" qualifiers="virtual const"> + <return type="int" /> + <argument index="0" name="shaped" type="RID" /> + <description> + Returns number of text spans added using [method _shaped_text_add_string] or [method _shaped_text_add_object]. + </description> + </method> + <method name="_shaped_get_span_meta" qualifiers="virtual const"> + <return type="Variant" /> + <argument index="0" name="shaped" type="RID" /> + <argument index="1" name="index" type="int" /> + <description> + Returns text span metadata. + </description> + </method> + <method name="_shaped_set_span_update_font" qualifiers="virtual"> + <return type="void" /> + <argument index="0" name="shaped" type="RID" /> + <argument index="1" name="index" type="int" /> + <argument index="2" name="fonts" type="Array" /> + <argument index="3" name="size" type="int" /> + <argument index="4" name="opentype_features" type="Dictionary" /> + <description> + Changes text span font, font size and OpenType features, without changing the text. + </description> + </method> <method name="_shaped_text_add_object" qualifiers="virtual"> <return type="bool" /> <argument index="0" name="shaped" type="RID" /> @@ -923,6 +949,7 @@ <argument index="3" name="size" type="int" /> <argument index="4" name="opentype_features" type="Dictionary" /> <argument index="5" name="language" type="String" /> + <argument index="6" name="meta" type="Variant" /> <description> Adds text span and font to draw it to the text buffer. </description> diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index dbbdd85706..433866602b 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -3289,7 +3289,7 @@ AnimationTrackEditGroup::AnimationTrackEditGroup() { ////////////////////////////////////// void AnimationTrackEditor::add_track_edit_plugin(const Ref<AnimationTrackEditPlugin> &p_plugin) { - if (track_edit_plugins.find(p_plugin) != -1) { + if (track_edit_plugins.has(p_plugin)) { return; } track_edit_plugins.push_back(p_plugin); diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 3d55f45dd6..e8fd97fa1a 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -467,7 +467,7 @@ void CreateDialog::select_type(const String &p_type) { } favorite->set_disabled(false); - favorite->set_pressed(favorite_list.find(p_type) != -1); + favorite->set_pressed(favorite_list.has(p_type)); get_ok_button()->set_disabled(false); } @@ -539,12 +539,12 @@ void CreateDialog::_favorite_toggled() { String name = item->get_text(0); - if (favorite_list.find(name) == -1) { - favorite_list.push_back(name); - favorite->set_pressed(true); - } else { + if (favorite_list.has(name)) { favorite_list.erase(name); favorite->set_pressed(false); + } else { + favorite_list.push_back(name); + favorite->set_pressed(true); } _save_and_update_favorite_list(); diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index 6acf654b04..749ab7e2f6 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -534,11 +534,11 @@ void DocTools::generate(bool p_basic_types) { Vector<Error> errs = ClassDB::get_method_error_return_values(name, E.name); if (errs.size()) { - if (errs.find(OK) == -1) { + if (!errs.has(OK)) { errs.insert(0, OK); } for (int i = 0; i < errs.size(); i++) { - if (method.errors_returned.find(errs[i]) == -1) { + if (!method.errors_returned.has(errs[i])) { method.errors_returned.push_back(errs[i]); } } diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index f97973ce5d..792897e451 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -1525,7 +1525,7 @@ void EditorExport::remove_export_preset(int p_idx) { } void EditorExport::add_export_plugin(const Ref<EditorExportPlugin> &p_plugin) { - if (export_plugins.find(p_plugin) == -1) { + if (!export_plugins.has(p_plugin)) { export_plugins.push_back(p_plugin); } } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index deafe65baf..75423f120d 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3725,7 +3725,7 @@ void EditorNode::_show_messages() { void EditorNode::_add_to_recent_scenes(const String &p_scene) { Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scenes", Array()); - if (rc.find(p_scene) != -1) { + if (rc.has(p_scene)) { rc.erase(p_scene); } rc.push_front(p_scene); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 8146f48f91..f230a9b435 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -369,7 +369,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // Skip locales which we can't render properly (see above comment). // Test against language code without regional variants (e.g. ur_PK). String lang_code = locale.get_slice("_", 0); - if (locales_to_skip.find(lang_code) != -1) { + if (locales_to_skip.has(lang_code)) { continue; } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index cd29a3e617..bc71072d29 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1801,7 +1801,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected // Add the files from favorites. Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); for (int i = 0; i < p_selected.size(); i++) { - if (favorites.find(p_selected[i]) == -1) { + if (!favorites.has(p_selected[i])) { favorites.push_back(p_selected[i]); } } @@ -2324,7 +2324,7 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, Vector<String> fnames = drag_data["files"]; Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); for (int i = 0; i < fnames.size(); i++) { - if (favorites.find(fnames[i]) == -1) { + if (!favorites.has(fnames[i])) { favorites.push_back(fnames[i]); } } diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 7ca61e83e7..d63366638e 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -517,7 +517,7 @@ bool ResourceImporterLayeredTexture::are_import_settings_valid(const String &p_p String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); bool test = ProjectSettings::get_singleton()->get(setting_path); if (test) { - if (formats_imported.find(compression_formats[index]) == -1) { + if (!formats_imported.has(compression_formats[index])) { valid = false; break; } diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index f21012258c..69c705ed5a 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -628,7 +628,7 @@ bool ResourceImporterTexture::are_import_settings_valid(const String &p_path) co String setting_path = "rendering/textures/vram_compression/import_" + String(compression_formats[index]); bool test = ProjectSettings::get_singleton()->get(setting_path); if (test) { - if (formats_imported.find(compression_formats[index]) == -1) { + if (!formats_imported.has(compression_formats[index])) { valid = false; break; } diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 419688fd9f..f809747410 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -447,7 +447,7 @@ static bool _find_owners(EditorFileSystemDirectory *efsd, const String &p_path) for (int i = 0; i < efsd->get_file_count(); i++) { Vector<String> deps = efsd->get_file_deps(i); - if (deps.find(p_path) != -1) { + if (deps.has(p_path)) { return true; } } diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index c0029312a7..f9df8db419 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -180,7 +180,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven selected_point = -1; for (int i = 0; i < points.size(); i++) { - if (making_triangle.find(i) != -1) { + if (making_triangle.has(i)) { continue; } diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 94990636da..649fc53b3a 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -755,7 +755,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //now scroll it to draw state_machine_draw->draw_style_box(sb, nr.node); - if (playing && (blend_from == name || current == name || travel_path.find(name) != -1)) { + if (playing && (blend_from == name || current == name || travel_path.has(name))) { state_machine_draw->draw_style_box(playing_overlay, nr.node); } diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 7a96e6eced..b116f8ff6d 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -473,7 +473,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_move_current = uv_mode; if (uv_move_current == UV_MODE_CREATE) { if (!uv_create) { - points_prev.resize(0); + points_prev.clear(); Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position())); points_prev.push_back(tuv); uv_create_to = tuv; @@ -682,7 +682,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } polygon_create.clear(); - } else if (polygon_create.find(closest) == -1) { + } else if (!polygon_create.has(closest)) { //add temporarily if not exists polygon_create.push_back(closest); } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 2a8882bbd1..9e46150813 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3486,7 +3486,7 @@ void ScriptEditor::_open_script_request(const String &p_path) { void ScriptEditor::register_syntax_highlighter(const Ref<EditorSyntaxHighlighter> &p_syntax_highlighter) { ERR_FAIL_COND(p_syntax_highlighter.is_null()); - if (syntax_highlighters.find(p_syntax_highlighter) == -1) { + if (!syntax_highlighters.has(p_syntax_highlighter)) { syntax_highlighters.push_back(p_syntax_highlighter); } } diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index dcda434093..89027b174d 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -848,9 +848,9 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over // Fade out the border of the grid. float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f); - float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f); + float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)(pos_in_rect.x + 1)), 0.0f, 1.0f); float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f); - float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f); + float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)(pos_in_rect.y + 1)), 0.0f, 1.0f); float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f); Transform2D tile_xform; @@ -2996,9 +2996,9 @@ void TileMapEditorTerrainsPlugin::forward_canvas_draw_over_viewport(Control *p_o // Fade out the border of the grid. float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f); - float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f); + float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)(pos_in_rect.x + 1)), 0.0f, 1.0f); float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f); - float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f); + float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)(pos_in_rect.y + 1)), 0.0f, 1.0f); float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f); Transform2D tile_xform; @@ -3881,9 +3881,9 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { // Fade out the border of the grid. float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f); - float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f); + float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)(pos_in_rect.x + 1)), 0.0f, 1.0f); float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f); - float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f); + float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)(pos_in_rect.y + 1)), 0.0f, 1.0f); float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f); Transform2D tile_xform; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index ef6d7b3462..61657397f1 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -1030,7 +1030,7 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { } void VisualShaderEditor::add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin) { - if (plugins.find(p_plugin) != -1) { + if (plugins.has(p_plugin)) { return; } plugins.push_back(p_plugin); diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index 3b0cbfdde9..7c0683c37b 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -185,7 +185,7 @@ void PropertySelector::_update_search() { continue; } - if (type_filter.size() && type_filter.find(E.type) == -1) { + if (type_filter.size() && !type_filter.has(E.type)) { continue; } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index cece787bf3..30ea1bcbb9 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1723,7 +1723,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V Node *validate = new_parent; while (validate) { - ERR_FAIL_COND_MSG(p_nodes.find(validate) != -1, "Selection changed at some point. Can't reparent."); + ERR_FAIL_COND_MSG(p_nodes.has(validate), "Selection changed at some point. Can't reparent."); validate = validate->get_parent(); } diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index e0ee809331..e50a5337cb 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -2901,7 +2901,7 @@ void TextServerAdvanced::font_set_global_oversampling(float p_oversampling) { List<RID> text_bufs; shaped_owner.get_owned_list(&text_bufs); for (const RID &E : text_bufs) { - invalidate(shaped_owner.get_or_null(E)); + invalidate(shaped_owner.get_or_null(E), false); } } } @@ -2936,7 +2936,7 @@ int TextServerAdvanced::_convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int return limit; } -void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped) { +void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped, bool p_text) { p_shaped->valid = false; p_shaped->sort_valid = false; p_shaped->line_breaks_valid = false; @@ -2951,27 +2951,32 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced * p_shaped->glyphs_logical.clear(); p_shaped->overrun_trim_data = TrimData(); p_shaped->utf16 = Char16String(); - if (p_shaped->script_iter != nullptr) { - memdelete(p_shaped->script_iter); - p_shaped->script_iter = nullptr; - } for (int i = 0; i < p_shaped->bidi_iter.size(); i++) { ubidi_close(p_shaped->bidi_iter[i]); } p_shaped->bidi_iter.clear(); + + if (p_text) { + if (p_shaped->script_iter != nullptr) { + memdelete(p_shaped->script_iter); + p_shaped->script_iter = nullptr; + } + p_shaped->break_ops_valid = false; + p_shaped->js_ops_valid = false; + } } void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) { ShapedTextDataAdvanced *parent = shaped_owner.get_or_null(p_shaped->parent); - for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) { + for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : parent->objects) { if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) { p_shaped->objects[E.key] = E.value; } } - for (int k = 0; k < parent->spans.size(); k++) { - ShapedTextDataAdvanced::Span span = parent->spans[k]; + for (int i = 0; i < parent->spans.size(); i++) { + ShapedTextDataAdvanced::Span span = parent->spans[i]; if (span.start >= p_shaped->end || span.end <= p_shaped->start) { continue; } @@ -3004,7 +3009,7 @@ void TextServerAdvanced::shaped_text_clear(RID p_shaped) { sd->spans.clear(); sd->objects.clear(); sd->bidi_override.clear(); - invalidate(sd); + invalidate(sd, true); } void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) { @@ -3017,7 +3022,7 @@ void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Dir full_copy(sd); } sd->direction = p_direction; - invalidate(sd); + invalidate(sd, false); } } @@ -3047,7 +3052,7 @@ void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const full_copy(sd); } sd->custom_punct = p_punct; - invalidate(sd); + invalidate(sd, false); } } @@ -3070,7 +3075,7 @@ void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Array for (int i = 0; i < p_override.size(); i++) { sd->bidi_override.push_back(p_override[i]); } - invalidate(sd); + invalidate(sd, false); } void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) { @@ -3083,7 +3088,7 @@ void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::O full_copy(sd); } sd->orientation = p_orientation; - invalidate(sd); + invalidate(sd, false); } } @@ -3095,7 +3100,7 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e ERR_FAIL_COND(sd->parent != RID()); if (sd->preserve_invalid != p_enabled) { sd->preserve_invalid = p_enabled; - invalidate(sd); + invalidate(sd, false); } } @@ -3117,7 +3122,7 @@ void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_e full_copy(sd); } sd->preserve_control = p_enabled; - invalidate(sd); + invalidate(sd, false); } } @@ -3137,7 +3142,41 @@ TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_sh return sd->orientation; } -bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { +int TextServerAdvanced::shaped_get_span_count(RID p_shaped) const { + ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V(!sd, 0); + return sd->spans.size(); +} + +Variant TextServerAdvanced::shaped_get_span_meta(RID p_shaped, int p_index) const { + ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V(!sd, Variant()); + ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant()); + return sd->spans[p_index].meta; +} + +void TextServerAdvanced::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) { + ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND(!sd); + ERR_FAIL_INDEX(p_index, sd->spans.size()); + + ShapedTextDataAdvanced::Span &span = sd->spans.write[p_index]; + bool changed = (span.font_size != p_size) || (span.features != p_opentype_features) || (p_fonts.size() != span.fonts.size()); + if (!changed) { + for (int i = 0; i < p_fonts.size(); i++) { + changed = changed || (span.fonts[i] != p_fonts[i]); + } + } + if (changed) { + span.fonts = p_fonts; + span.font_size = p_size; + span.features = p_opentype_features; + + invalidate(sd, false); + } +} + +bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); ERR_FAIL_COND_V(p_size <= 0, false); @@ -3162,11 +3201,12 @@ bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_te span.font_size = p_size; span.language = p_language; span.features = p_opentype_features; + span.meta = p_meta; sd->spans.push_back(span); sd->text += p_text; sd->end += p_text.length(); - invalidate(sd); + invalidate(sd, true); return true; } @@ -3196,13 +3236,13 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con sd->text += String::chr(0xfffc).repeat(p_length); sd->end += p_length; sd->objects[p_key] = obj; - invalidate(sd); + invalidate(sd, true); return true; } bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); @@ -3222,7 +3262,7 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, Glyph gl = sd->glyphs[i]; Variant key; if (gl.count == 1) { - for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) { + for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) { if (E.value.pos == gl.start) { key = E.key; break; @@ -3262,75 +3302,78 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->width += gl.advance * gl.repeat; } } + _realign(sd); + } + return true; +} - // Align embedded objects to baseline. - float full_ascent = sd->ascent; - float full_descent = sd->descent; - for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) { - if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) { - if (sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { - case INLINE_ALIGNMENT_TO_TOP: { - E.value.rect.position.y = -sd->ascent; - } break; - case INLINE_ALIGNMENT_TO_CENTER: { - E.value.rect.position.y = (-sd->ascent + sd->descent) / 2; - } break; - case INLINE_ALIGNMENT_TO_BASELINE: { - E.value.rect.position.y = 0; - } break; - case INLINE_ALIGNMENT_TO_BOTTOM: { - E.value.rect.position.y = sd->descent; - } break; - } - switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { - case INLINE_ALIGNMENT_BOTTOM_TO: { - E.value.rect.position.y -= E.value.rect.size.y; - } break; - case INLINE_ALIGNMENT_CENTER_TO: { - E.value.rect.position.y -= E.value.rect.size.y / 2; - } break; - case INLINE_ALIGNMENT_TOP_TO: { - // NOP - } break; - } - full_ascent = MAX(full_ascent, -E.value.rect.position.y); - full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y); - } else { - switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { - case INLINE_ALIGNMENT_TO_TOP: { - E.value.rect.position.x = -sd->ascent; - } break; - case INLINE_ALIGNMENT_TO_CENTER: { - E.value.rect.position.x = (-sd->ascent + sd->descent) / 2; - } break; - case INLINE_ALIGNMENT_TO_BASELINE: { - E.value.rect.position.x = 0; - } break; - case INLINE_ALIGNMENT_TO_BOTTOM: { - E.value.rect.position.x = sd->descent; - } break; - } - switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { - case INLINE_ALIGNMENT_BOTTOM_TO: { - E.value.rect.position.x -= E.value.rect.size.x; - } break; - case INLINE_ALIGNMENT_CENTER_TO: { - E.value.rect.position.x -= E.value.rect.size.x / 2; - } break; - case INLINE_ALIGNMENT_TOP_TO: { - // NOP - } break; - } - full_ascent = MAX(full_ascent, -E.value.rect.position.x); - full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x); +void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const { + // Align embedded objects to baseline. + float full_ascent = p_sd->ascent; + float full_descent = p_sd->descent; + for (KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) { + if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) { + if (p_sd->orientation == ORIENTATION_HORIZONTAL) { + switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { + case INLINE_ALIGNMENT_TO_TOP: { + E.value.rect.position.y = -p_sd->ascent; + } break; + case INLINE_ALIGNMENT_TO_CENTER: { + E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2; + } break; + case INLINE_ALIGNMENT_TO_BASELINE: { + E.value.rect.position.y = 0; + } break; + case INLINE_ALIGNMENT_TO_BOTTOM: { + E.value.rect.position.y = p_sd->descent; + } break; + } + switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { + case INLINE_ALIGNMENT_BOTTOM_TO: { + E.value.rect.position.y -= E.value.rect.size.y; + } break; + case INLINE_ALIGNMENT_CENTER_TO: { + E.value.rect.position.y -= E.value.rect.size.y / 2; + } break; + case INLINE_ALIGNMENT_TOP_TO: { + // NOP + } break; + } + full_ascent = MAX(full_ascent, -E.value.rect.position.y); + full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y); + } else { + switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { + case INLINE_ALIGNMENT_TO_TOP: { + E.value.rect.position.x = -p_sd->ascent; + } break; + case INLINE_ALIGNMENT_TO_CENTER: { + E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2; + } break; + case INLINE_ALIGNMENT_TO_BASELINE: { + E.value.rect.position.x = 0; + } break; + case INLINE_ALIGNMENT_TO_BOTTOM: { + E.value.rect.position.x = p_sd->descent; + } break; + } + switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { + case INLINE_ALIGNMENT_BOTTOM_TO: { + E.value.rect.position.x -= E.value.rect.size.x; + } break; + case INLINE_ALIGNMENT_CENTER_TO: { + E.value.rect.position.x -= E.value.rect.size.x / 2; + } break; + case INLINE_ALIGNMENT_TOP_TO: { + // NOP + } break; } + full_ascent = MAX(full_ascent, -E.value.rect.position.x); + full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x); } } - sd->ascent = full_ascent; - sd->descent = full_descent; } - return true; + p_sd->ascent = full_ascent; + p_sd->descent = full_descent; } RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const { @@ -3423,7 +3466,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S Variant key; bool find_embedded = false; if (gl.count == 1) { - for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_sd->objects) { + for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : p_sd->objects) { if (E.value.pos == gl.start) { find_embedded = true; key = E.key; @@ -3466,72 +3509,7 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S } } - // Align embedded objects to baseline. - float full_ascent = p_new_sd->ascent; - float full_descent = p_new_sd->descent; - for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_new_sd->objects) { - if ((E.value.pos >= p_new_sd->start) && (E.value.pos < p_new_sd->end)) { - if (p_sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { - case INLINE_ALIGNMENT_TO_TOP: { - E.value.rect.position.y = -p_new_sd->ascent; - } break; - case INLINE_ALIGNMENT_TO_CENTER: { - E.value.rect.position.y = (-p_new_sd->ascent + p_new_sd->descent) / 2; - } break; - case INLINE_ALIGNMENT_TO_BASELINE: { - E.value.rect.position.y = 0; - } break; - case INLINE_ALIGNMENT_TO_BOTTOM: { - E.value.rect.position.y = p_new_sd->descent; - } break; - } - switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { - case INLINE_ALIGNMENT_BOTTOM_TO: { - E.value.rect.position.y -= E.value.rect.size.y; - } break; - case INLINE_ALIGNMENT_CENTER_TO: { - E.value.rect.position.y -= E.value.rect.size.y / 2; - } break; - case INLINE_ALIGNMENT_TOP_TO: { - // NOP - } break; - } - full_ascent = MAX(full_ascent, -E.value.rect.position.y); - full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y); - } else { - switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { - case INLINE_ALIGNMENT_TO_TOP: { - E.value.rect.position.x = -p_new_sd->ascent; - } break; - case INLINE_ALIGNMENT_TO_CENTER: { - E.value.rect.position.x = (-p_new_sd->ascent + p_new_sd->descent) / 2; - } break; - case INLINE_ALIGNMENT_TO_BASELINE: { - E.value.rect.position.x = 0; - } break; - case INLINE_ALIGNMENT_TO_BOTTOM: { - E.value.rect.position.x = p_new_sd->descent; - } break; - } - switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { - case INLINE_ALIGNMENT_BOTTOM_TO: { - E.value.rect.position.x -= E.value.rect.size.x; - } break; - case INLINE_ALIGNMENT_CENTER_TO: { - E.value.rect.position.x -= E.value.rect.size.x / 2; - } break; - case INLINE_ALIGNMENT_TOP_TO: { - // NOP - } break; - } - full_ascent = MAX(full_ascent, -E.value.rect.position.x); - full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x); - } - } - } - p_new_sd->ascent = full_ascent; - p_new_sd->descent = full_descent; + _realign(p_new_sd); } p_new_sd->valid = true; @@ -3968,40 +3946,43 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) { const UChar *data = sd->utf16.ptr(); - HashMap<int, bool> breaks; - UErrorCode err = U_ZERO_ERROR; - int i = 0; - while (i < sd->spans.size()) { - String language = sd->spans[i].language; - int r_start = sd->spans[i].start; - while (i + 1 < sd->spans.size() && language == sd->spans[i + 1].language) { - i++; - } - int r_end = sd->spans[i].end; - UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err); - if (U_FAILURE(err)) { - // No data loaded - use fallback. - for (int j = r_start; j < r_end; j++) { - char32_t c = sd->text[j - sd->start]; - if (is_whitespace(c)) { - breaks[j + 1] = false; - } - if (is_linebreak(c)) { - breaks[j + 1] = true; - } + if (!sd->break_ops_valid) { + sd->breaks.clear(); + UErrorCode err = U_ZERO_ERROR; + int i = 0; + while (i < sd->spans.size()) { + String language = sd->spans[i].language; + int r_start = sd->spans[i].start; + while (i + 1 < sd->spans.size() && language == sd->spans[i + 1].language) { + i++; } - } else { - while (ubrk_next(bi) != UBRK_DONE) { - int pos = _convert_pos(sd, ubrk_current(bi)) + r_start; - if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) { - breaks[pos] = true; - } else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) { - breaks[pos] = false; + int r_end = sd->spans[i].end; + UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err); + if (U_FAILURE(err)) { + // No data loaded - use fallback. + for (int j = r_start; j < r_end; j++) { + char32_t c = sd->text[j - sd->start]; + if (is_whitespace(c)) { + sd->breaks[j + 1] = false; + } + if (is_linebreak(c)) { + sd->breaks[j + 1] = true; + } + } + } else { + while (ubrk_next(bi) != UBRK_DONE) { + int pos = _convert_pos(sd, ubrk_current(bi)) + r_start; + if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) { + sd->breaks[pos] = true; + } else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) { + sd->breaks[pos] = false; + } } } + ubrk_close(bi); + i++; } - ubrk_close(bi); - i++; + sd->break_ops_valid = true; } sd->sort_valid = false; @@ -4013,7 +3994,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) { int c_punct_size = sd->custom_punct.length(); const char32_t *c_punct = sd->custom_punct.ptr(); - for (i = 0; i < sd_size; i++) { + for (int i = 0; i < sd_size; i++) { if (sd_glyphs[i].count > 0) { char32_t c = ch[sd_glyphs[i].start - sd->start]; if (c == 0xfffc) { @@ -4040,8 +4021,8 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) { if (is_underscore(c)) { sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE; } - if (breaks.has(sd_glyphs[i].end)) { - if (breaks[sd_glyphs[i].end] && (is_linebreak(c))) { + if (sd->breaks.has(sd_glyphs[i].end)) { + if (sd->breaks[sd_glyphs[i].end] && (is_linebreak(c))) { sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD; } else if (is_whitespace(c)) { sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT; @@ -4186,41 +4167,45 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { const UChar *data = sd->utf16.ptr(); int32_t data_size = sd->utf16.length(); - Map<int, bool> jstops; + if (!sd->js_ops_valid) { + sd->jstops.clear(); - // Use ICU word iterator and custom kashida detection. - UErrorCode err = U_ZERO_ERROR; - UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err); - if (U_FAILURE(err)) { - // No data - use fallback. - int limit = 0; - for (int i = 0; i < sd->text.length(); i++) { - if (is_whitespace(data[i])) { - int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start; - if (ks != -1) { - jstops[ks] = true; - } - limit = i + 1; + // Use ICU word iterator and custom kashida detection. + UErrorCode err = U_ZERO_ERROR; + UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err); + if (U_FAILURE(err)) { + // No data - use fallback. + int limit = 0; + for (int i = 0; i < sd->text.length(); i++) { + if (is_whitespace(data[i])) { + int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start; + if (ks != -1) { + sd->jstops[ks] = true; + } + limit = i + 1; + } } - } - int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start; - if (ks != -1) { - jstops[ks] = true; - } - } else { - int limit = 0; - while (ubrk_next(bi) != UBRK_DONE) { - if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) { - int i = _convert_pos(sd, ubrk_current(bi)); - jstops[i + sd->start] = false; - int ks = _generate_kashida_justification_opportunies(sd->text, limit, i); - if (ks != -1) { - jstops[ks + sd->start] = true; - } - limit = i; + int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start; + if (ks != -1) { + sd->jstops[ks] = true; } + } else { + int limit = 0; + while (ubrk_next(bi) != UBRK_DONE) { + if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) { + int i = _convert_pos(sd, ubrk_current(bi)); + sd->jstops[i + sd->start] = false; + int ks = _generate_kashida_justification_opportunies(sd->text, limit, i); + if (ks != -1) { + sd->jstops[ks + sd->start] = true; + } + limit = i; + } + } + ubrk_close(bi); } - ubrk_close(bi); + + sd->js_ops_valid = true; } sd->sort_valid = false; @@ -4228,18 +4213,18 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) { Glyph *sd_glyphs = sd->glyphs.ptrw(); int sd_size = sd->glyphs.size(); - if (jstops.size() > 0) { + if (sd->jstops.size() > 0) { for (int i = 0; i < sd_size; i++) { if (sd_glyphs[i].count > 0) { char32_t c = sd->text[sd_glyphs[i].start - sd->start]; if (c == 0x0640) { sd_glyphs[i].flags |= GRAPHEME_IS_ELONGATION; } - if (jstops.has(sd_glyphs[i].start)) { + if (sd->jstops.has(sd_glyphs[i].start)) { if (c == 0xfffc) { continue; } - if (jstops[sd_glyphs[i].start]) { + if (sd->jstops[sd_glyphs[i].start]) { if (c != 0x0640) { if (sd_glyphs[i].font_rid != RID()) { Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size); @@ -4574,7 +4559,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { return true; } - invalidate(sd); + invalidate(sd, false); if (sd->parent != RID()) { shaped_text_shape(sd->parent); ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent); @@ -4733,70 +4718,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) { } } - // Align embedded objects to baseline. - float full_ascent = sd->ascent; - float full_descent = sd->descent; - for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) { - if (sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { - case INLINE_ALIGNMENT_TO_TOP: { - E.value.rect.position.y = -sd->ascent; - } break; - case INLINE_ALIGNMENT_TO_CENTER: { - E.value.rect.position.y = (-sd->ascent + sd->descent) / 2; - } break; - case INLINE_ALIGNMENT_TO_BASELINE: { - E.value.rect.position.y = 0; - } break; - case INLINE_ALIGNMENT_TO_BOTTOM: { - E.value.rect.position.y = sd->descent; - } break; - } - switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { - case INLINE_ALIGNMENT_BOTTOM_TO: { - E.value.rect.position.y -= E.value.rect.size.y; - } break; - case INLINE_ALIGNMENT_CENTER_TO: { - E.value.rect.position.y -= E.value.rect.size.y / 2; - } break; - case INLINE_ALIGNMENT_TOP_TO: { - // NOP - } break; - } - full_ascent = MAX(full_ascent, -E.value.rect.position.y); - full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y); - } else { - switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { - case INLINE_ALIGNMENT_TO_TOP: { - E.value.rect.position.x = -sd->ascent; - } break; - case INLINE_ALIGNMENT_TO_CENTER: { - E.value.rect.position.x = (-sd->ascent + sd->descent) / 2; - } break; - case INLINE_ALIGNMENT_TO_BASELINE: { - E.value.rect.position.x = 0; - } break; - case INLINE_ALIGNMENT_TO_BOTTOM: { - E.value.rect.position.x = sd->descent; - } break; - } - switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { - case INLINE_ALIGNMENT_BOTTOM_TO: { - E.value.rect.position.x -= E.value.rect.size.x; - } break; - case INLINE_ALIGNMENT_CENTER_TO: { - E.value.rect.position.x -= E.value.rect.size.x / 2; - } break; - case INLINE_ALIGNMENT_TOP_TO: { - // NOP - } break; - } - full_ascent = MAX(full_ascent, -E.value.rect.position.x); - full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x); - } - } - sd->ascent = full_ascent; - sd->descent = full_descent; + _realign(sd); sd->valid = true; return sd->valid; } @@ -4863,7 +4785,7 @@ Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const { ERR_FAIL_COND_V(!sd, ret); MutexLock lock(sd->mutex); - for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) { + for (const KeyValue<Variant, ShapedTextDataAdvanced::EmbeddedObject> &E : sd->objects) { ret.push_back(E.key); } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index f2ae24cae6..145d740b68 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -242,6 +242,21 @@ class TextServerAdvanced : public TextServer { // Shaped text cache data. struct ShapedTextDataAdvanced : public ShapedTextData { + struct Span { + int start = -1; + int end = -1; + + Vector<RID> fonts; + int font_size = 0; + + Variant embedded_key; + + String language; + Dictionary features; + Variant meta; + }; + Vector<Span> spans; + /* Intermediate data */ Char16String utf16; Vector<UBiDi *> bidi_iter; @@ -249,6 +264,11 @@ class TextServerAdvanced : public TextServer { ScriptIterator *script_iter = nullptr; hb_buffer_t *hb_buffer = nullptr; + HashMap<int, bool> jstops; + HashMap<int, bool> breaks; + bool break_ops_valid = false; + bool js_ops_valid = false; + ~ShapedTextDataAdvanced() { for (int i = 0; i < bidi_iter.size(); i++) { ubidi_close(bidi_iter[i]); @@ -268,6 +288,7 @@ class TextServerAdvanced : public TextServer { mutable RID_PtrOwner<FontDataAdvanced> font_owner; mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner; + void _realign(ShapedTextDataAdvanced *p_sd) const; int _convert_pos(const ShapedTextDataAdvanced *p_sd, int p_pos) const; int _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int p_pos) const; bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int p_start, int p_length) const; @@ -302,7 +323,7 @@ protected: static void _bind_methods(){}; void full_copy(ShapedTextDataAdvanced *p_shaped); - void invalidate(ShapedTextDataAdvanced *p_shaped); + void invalidate(ShapedTextDataAdvanced *p_shaped, bool p_text = false); public: virtual bool has_feature(Feature p_feature) const override; @@ -482,10 +503,14 @@ public: virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override; virtual bool shaped_text_get_preserve_control(RID p_shaped) const override; - virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override; + virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override; virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override; virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override; + virtual int shaped_get_span_count(RID p_shaped) const override; + virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override; + virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override; + virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override; virtual RID shaped_text_get_parent(RID p_shaped) const override; diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 4a0415fbf9..dd520a2e40 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -91,7 +91,7 @@ void TextServerFallback::free(RID p_rid) { font_owner.free(p_rid); memdelete(fd); } else if (shaped_owner.owns(p_rid)) { - ShapedTextData *sd = shaped_owner.get_or_null(p_rid); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_rid); shaped_owner.free(p_rid); memdelete(sd); } @@ -2061,7 +2061,7 @@ void TextServerFallback::font_set_global_oversampling(float p_oversampling) { /* Shaped text buffer interface */ /*************************************************************************/ -void TextServerFallback::invalidate(ShapedTextData *p_shaped) { +void TextServerFallback::invalidate(ShapedTextDataFallback *p_shaped) { p_shaped->valid = false; p_shaped->sort_valid = false; p_shaped->line_breaks_valid = false; @@ -2075,17 +2075,17 @@ void TextServerFallback::invalidate(ShapedTextData *p_shaped) { p_shaped->glyphs_logical.clear(); } -void TextServerFallback::full_copy(ShapedTextData *p_shaped) { - ShapedTextData *parent = shaped_owner.get_or_null(p_shaped->parent); +void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) { + ShapedTextDataFallback *parent = shaped_owner.get_or_null(p_shaped->parent); - for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) { + for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : parent->objects) { if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) { p_shaped->objects[E.key] = E.value; } } for (int k = 0; k < parent->spans.size(); k++) { - ShapedTextData::Span span = parent->spans[k]; + ShapedTextDataFallback::Span span = parent->spans[k]; if (span.start >= p_shaped->end || span.end <= p_shaped->start) { continue; } @@ -2099,7 +2099,7 @@ void TextServerFallback::full_copy(ShapedTextData *p_shaped) { RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) { _THREAD_SAFE_METHOD_ - ShapedTextData *sd = memnew(ShapedTextData); + ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback); sd->direction = p_direction; sd->orientation = p_orientation; @@ -2107,7 +2107,7 @@ RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, Te } void TextServerFallback::shaped_text_clear(RID p_shaped) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); MutexLock lock(sd->mutex); @@ -2136,7 +2136,7 @@ TextServer::Direction TextServerFallback::shaped_text_get_inferred_direction(RID void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) { _THREAD_SAFE_METHOD_ - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); if (sd->custom_punct != p_punct) { @@ -2150,13 +2150,13 @@ void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String TextServerFallback::shaped_text_get_custom_punctuation(RID p_shaped) const { _THREAD_SAFE_METHOD_ - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, String()); return sd->custom_punct; } void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); MutexLock lock(sd->mutex); @@ -2174,7 +2174,7 @@ void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Array } TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL); MutexLock lock(sd->mutex); @@ -2182,7 +2182,7 @@ TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_sh } void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); MutexLock lock(sd->mutex); ERR_FAIL_COND(!sd); @@ -2196,7 +2196,7 @@ void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e } bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); @@ -2204,7 +2204,7 @@ bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const { } void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND(!sd); MutexLock lock(sd->mutex); @@ -2218,15 +2218,52 @@ void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_e } bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); return sd->preserve_control; } -bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); +int TextServerFallback::shaped_get_span_count(RID p_shaped) const { + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V(!sd, 0); + return sd->spans.size(); +} + +Variant TextServerFallback::shaped_get_span_meta(RID p_shaped, int p_index) const { + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V(!sd, Variant()); + ERR_FAIL_INDEX_V(p_index, sd->spans.size(), Variant()); + return sd->spans[p_index].meta; +} + +void TextServerFallback::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) { + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND(!sd); + ERR_FAIL_INDEX(p_index, sd->spans.size()); + + ShapedTextDataFallback::Span &span = sd->spans.write[p_index]; + span.fonts.clear(); + // Pre-sort fonts, push fonts with the language support first. + Vector<RID> fonts_no_match; + int font_count = p_fonts.size(); + for (int i = 0; i < font_count; i++) { + if (font_is_language_supported(p_fonts[i], span.language)) { + span.fonts.push_back(p_fonts[i]); + } else { + fonts_no_match.push_back(p_fonts[i]); + } + } + span.fonts.append_array(fonts_no_match); + span.font_size = p_size; + span.features = p_opentype_features; + + sd->valid = false; +} + +bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); @@ -2244,7 +2281,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te full_copy(sd); } - ShapedTextData::Span span; + ShapedTextDataFallback::Span span; span.start = sd->text.length(); span.end = span.start + p_text.length(); @@ -2263,6 +2300,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te ERR_FAIL_COND_V(span.fonts.is_empty(), false); span.font_size = p_size; span.language = p_language; + span.meta = p_meta; sd->spans.push_back(span); sd->text += p_text; @@ -2273,7 +2311,7 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te } bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); @@ -2284,12 +2322,12 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con full_copy(sd); } - ShapedTextData::Span span; + ShapedTextDataFallback::Span span; span.start = sd->start + sd->text.length(); span.end = span.start + p_length; span.embedded_key = p_key; - ShapedTextData::EmbeddedObject obj; + ShapedTextDataFallback::EmbeddedObject obj; obj.inline_align = p_inline_align; obj.rect.size = p_size; obj.pos = span.start; @@ -2304,7 +2342,7 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con } bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); @@ -2324,7 +2362,7 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, Glyph gl = sd->glyphs[i]; Variant key; if (gl.count == 1) { - for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) { + for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) { if (E.value.pos == gl.start) { key = E.key; break; @@ -2364,79 +2402,82 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, sd->width += gl.advance * gl.repeat; } } + _realign(sd); + } + return true; +} - // Align embedded objects to baseline. - float full_ascent = sd->ascent; - float full_descent = sd->descent; - for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) { - if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) { - if (sd->orientation == ORIENTATION_HORIZONTAL) { - switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { - case INLINE_ALIGNMENT_TO_TOP: { - E.value.rect.position.y = -sd->ascent; - } break; - case INLINE_ALIGNMENT_TO_CENTER: { - E.value.rect.position.y = (-sd->ascent + sd->descent) / 2; - } break; - case INLINE_ALIGNMENT_TO_BASELINE: { - E.value.rect.position.y = 0; - } break; - case INLINE_ALIGNMENT_TO_BOTTOM: { - E.value.rect.position.y = sd->descent; - } break; - } - switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { - case INLINE_ALIGNMENT_BOTTOM_TO: { - E.value.rect.position.y -= E.value.rect.size.y; - } break; - case INLINE_ALIGNMENT_CENTER_TO: { - E.value.rect.position.y -= E.value.rect.size.y / 2; - } break; - case INLINE_ALIGNMENT_TOP_TO: { - // NOP - } break; - } - full_ascent = MAX(full_ascent, -E.value.rect.position.y); - full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y); - } else { - switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { - case INLINE_ALIGNMENT_TO_TOP: { - E.value.rect.position.x = -sd->ascent; - } break; - case INLINE_ALIGNMENT_TO_CENTER: { - E.value.rect.position.x = (-sd->ascent + sd->descent) / 2; - } break; - case INLINE_ALIGNMENT_TO_BASELINE: { - E.value.rect.position.x = 0; - } break; - case INLINE_ALIGNMENT_TO_BOTTOM: { - E.value.rect.position.x = sd->descent; - } break; - } - switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { - case INLINE_ALIGNMENT_BOTTOM_TO: { - E.value.rect.position.x -= E.value.rect.size.x; - } break; - case INLINE_ALIGNMENT_CENTER_TO: { - E.value.rect.position.x -= E.value.rect.size.x / 2; - } break; - case INLINE_ALIGNMENT_TOP_TO: { - // NOP - } break; - } - full_ascent = MAX(full_ascent, -E.value.rect.position.x); - full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x); +void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const { + // Align embedded objects to baseline. + float full_ascent = p_sd->ascent; + float full_descent = p_sd->descent; + for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : p_sd->objects) { + if ((E.value.pos >= p_sd->start) && (E.value.pos < p_sd->end)) { + if (p_sd->orientation == ORIENTATION_HORIZONTAL) { + switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { + case INLINE_ALIGNMENT_TO_TOP: { + E.value.rect.position.y = -p_sd->ascent; + } break; + case INLINE_ALIGNMENT_TO_CENTER: { + E.value.rect.position.y = (-p_sd->ascent + p_sd->descent) / 2; + } break; + case INLINE_ALIGNMENT_TO_BASELINE: { + E.value.rect.position.y = 0; + } break; + case INLINE_ALIGNMENT_TO_BOTTOM: { + E.value.rect.position.y = p_sd->descent; + } break; + } + switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { + case INLINE_ALIGNMENT_BOTTOM_TO: { + E.value.rect.position.y -= E.value.rect.size.y; + } break; + case INLINE_ALIGNMENT_CENTER_TO: { + E.value.rect.position.y -= E.value.rect.size.y / 2; + } break; + case INLINE_ALIGNMENT_TOP_TO: { + // NOP + } break; } + full_ascent = MAX(full_ascent, -E.value.rect.position.y); + full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y); + } else { + switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { + case INLINE_ALIGNMENT_TO_TOP: { + E.value.rect.position.x = -p_sd->ascent; + } break; + case INLINE_ALIGNMENT_TO_CENTER: { + E.value.rect.position.x = (-p_sd->ascent + p_sd->descent) / 2; + } break; + case INLINE_ALIGNMENT_TO_BASELINE: { + E.value.rect.position.x = 0; + } break; + case INLINE_ALIGNMENT_TO_BOTTOM: { + E.value.rect.position.x = p_sd->descent; + } break; + } + switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) { + case INLINE_ALIGNMENT_BOTTOM_TO: { + E.value.rect.position.x -= E.value.rect.size.x; + } break; + case INLINE_ALIGNMENT_CENTER_TO: { + E.value.rect.position.x -= E.value.rect.size.x / 2; + } break; + case INLINE_ALIGNMENT_TOP_TO: { + // NOP + } break; + } + full_ascent = MAX(full_ascent, -E.value.rect.position.x); + full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x); } } - sd->ascent = full_ascent; - sd->descent = full_descent; } - return true; + p_sd->ascent = full_ascent; + p_sd->descent = full_descent; } RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_length) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, RID()); MutexLock lock(sd->mutex); @@ -2450,7 +2491,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID()); ERR_FAIL_COND_V(sd->end < p_start + p_length, RID()); - ShapedTextData *new_sd = memnew(ShapedTextData); + ShapedTextDataFallback *new_sd = memnew(ShapedTextDataFallback); new_sd->parent = p_shaped; new_sd->start = p_start; new_sd->end = p_start + p_length; @@ -2476,7 +2517,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng Variant key; bool find_embedded = false; if (gl.count == 1) { - for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) { + for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) { if (E.value.pos == gl.start) { find_embedded = true; key = E.key; @@ -2520,7 +2561,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng // Align embedded objects to baseline. float full_ascent = new_sd->ascent; float full_descent = new_sd->descent; - for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : new_sd->objects) { + for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : new_sd->objects) { if ((E.value.pos >= new_sd->start) && (E.value.pos < new_sd->end)) { if (sd->orientation == ORIENTATION_HORIZONTAL) { switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { @@ -2590,7 +2631,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng } RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, RID()); MutexLock lock(sd->mutex); @@ -2598,7 +2639,7 @@ RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const { } float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, uint16_t /*JustificationFlag*/ p_jst_flags) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); MutexLock lock(sd->mutex); @@ -2707,7 +2748,7 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, } float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); MutexLock lock(sd->mutex); @@ -2763,7 +2804,7 @@ float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat3 } bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); @@ -2819,7 +2860,7 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) { } bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); @@ -2835,7 +2876,7 @@ bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) { } void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint16_t p_trim_flags) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped_line); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped_line); ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid."); MutexLock lock(sd->mutex); @@ -2863,9 +2904,9 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl return; } - Vector<ShapedTextData::Span> &spans = sd->spans; + Vector<ShapedTextDataFallback::Span> &spans = sd->spans; if (sd->parent != RID()) { - ShapedTextData *parent_sd = shaped_owner.get_or_null(sd->parent); + ShapedTextDataFallback *parent_sd = shaped_owner.get_or_null(sd->parent); ERR_FAIL_COND(!parent_sd->valid); spans = parent_sd->spans; } @@ -2987,39 +3028,39 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl } int TextServerFallback::shaped_text_get_trim_pos(RID p_shaped) const { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); - ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid."); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid."); MutexLock lock(sd->mutex); return sd->overrun_trim_data.trim_pos; } int TextServerFallback::shaped_text_get_ellipsis_pos(RID p_shaped) const { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); - ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid."); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataFallback invalid."); MutexLock lock(sd->mutex); return sd->overrun_trim_data.ellipsis_pos; } const Glyph *TextServerFallback::shaped_text_get_ellipsis_glyphs(RID p_shaped) const { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); - ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextData invalid."); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextDataFallback invalid."); MutexLock lock(sd->mutex); return sd->overrun_trim_data.ellipsis_glyph_buf.ptr(); } int TextServerFallback::shaped_text_get_ellipsis_glyph_count(RID p_shaped) const { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); - ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextData invalid."); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); + ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextDataFallback invalid."); MutexLock lock(sd->mutex); return sd->overrun_trim_data.ellipsis_glyph_buf.size(); } bool TextServerFallback::shaped_text_shape(RID p_shaped) { - ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); @@ -3046,7 +3087,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { // "Shape" string. for (int i = 0; i < sd->spans.size(); i++) { - const ShapedTextData::Span &span = sd->spans[i]; + const ShapedTextDataFallback::Span &span = sd->spans[i]; if (span.embedded_key != Variant()) { // Embedded object. if (sd->orientation == ORIENTATION_HORIZONTAL) { @@ -3146,7 +3187,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { // Align embedded objects to baseline. float full_ascent = sd->ascent; float full_descent = sd->descent; - for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) { + for (KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) { if (sd->orientation == ORIENTATION_HORIZONTAL) { switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) { case INLINE_ALIGNMENT_TO_TOP: { @@ -3212,7 +3253,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) { } bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, false); MutexLock lock(sd->mutex); @@ -3220,7 +3261,7 @@ bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const { } const Glyph *TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, nullptr); MutexLock lock(sd->mutex); @@ -3231,7 +3272,7 @@ const Glyph *TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const { } int TextServerFallback::shaped_text_get_glyph_count(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0); MutexLock lock(sd->mutex); @@ -3242,7 +3283,7 @@ int TextServerFallback::shaped_text_get_glyph_count(RID p_shaped) const { } const Glyph *TextServerFallback::shaped_text_sort_logical(RID p_shaped) { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, nullptr); MutexLock lock(sd->mutex); @@ -3254,7 +3295,7 @@ const Glyph *TextServerFallback::shaped_text_sort_logical(RID p_shaped) { } Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, Vector2i()); MutexLock lock(sd->mutex); @@ -3263,11 +3304,11 @@ Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const { Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const { Array ret; - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, ret); MutexLock lock(sd->mutex); - for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) { + for (const KeyValue<Variant, ShapedTextDataFallback::EmbeddedObject> &E : sd->objects) { ret.push_back(E.key); } @@ -3275,7 +3316,7 @@ Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const { } Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, Rect2()); MutexLock lock(sd->mutex); @@ -3287,7 +3328,7 @@ Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_ke } Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, Size2()); MutexLock lock(sd->mutex); @@ -3302,7 +3343,7 @@ Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const { } float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); MutexLock lock(sd->mutex); @@ -3313,7 +3354,7 @@ float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const { } float TextServerFallback::shaped_text_get_descent(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); MutexLock lock(sd->mutex); @@ -3324,7 +3365,7 @@ float TextServerFallback::shaped_text_get_descent(RID p_shaped) const { } float TextServerFallback::shaped_text_get_width(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); MutexLock lock(sd->mutex); @@ -3335,7 +3376,7 @@ float TextServerFallback::shaped_text_get_width(RID p_shaped) const { } float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); MutexLock lock(sd->mutex); @@ -3347,7 +3388,7 @@ float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const } float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const { - const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped); + const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped); ERR_FAIL_COND_V(!sd, 0.f); MutexLock lock(sd->mutex); diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index b356b90d2c..be944cde58 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -203,17 +203,38 @@ class TextServerFallback : public TextServer { } } + // Shaped text cache data. + + struct ShapedTextDataFallback : public ShapedTextData { + struct Span { + int start = -1; + int end = -1; + + Vector<RID> fonts; + int font_size = 0; + + Variant embedded_key; + + String language; + Dictionary features; + Variant meta; + }; + Vector<Span> spans; + }; + // Common data. float oversampling = 1.f; mutable RID_PtrOwner<FontDataFallback> font_owner; - mutable RID_PtrOwner<ShapedTextData> shaped_owner; + mutable RID_PtrOwner<ShapedTextDataFallback> shaped_owner; + + void _realign(ShapedTextDataFallback *p_sd) const; protected: static void _bind_methods(){}; - void full_copy(ShapedTextData *p_shaped); - void invalidate(ShapedTextData *p_shaped); + void full_copy(ShapedTextDataFallback *p_shaped); + void invalidate(ShapedTextDataFallback *p_shaped); public: virtual bool has_feature(Feature p_feature) const override; @@ -391,10 +412,14 @@ public: virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override; virtual bool shaped_text_get_preserve_control(RID p_shaped) const override; - virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override; + virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override; virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override; virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override; + virtual int shaped_get_span_count(RID p_shaped) const override; + virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override; + virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override; + virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override; virtual RID shaped_text_get_parent(RID p_shaped) const override; diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp index 9d297fcb93..a9652cbba1 100644 --- a/modules/theora/video_stream_theora.cpp +++ b/modules/theora/video_stream_theora.cpp @@ -337,6 +337,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) { Ref<Image> img; img.instantiate(); img->create(w, h, false, Image::FORMAT_RGBA8); + texture->create_from_image(img); } else { /* tear down the partial theora setup */ diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp index 4916eb573c..decb3d0dd8 100644 --- a/scene/2d/animated_sprite_2d.cpp +++ b/scene/2d/animated_sprite_2d.cpp @@ -417,7 +417,7 @@ void AnimatedSprite2D::_reset_timeout() { void AnimatedSprite2D::set_animation(const StringName &p_animation) { ERR_FAIL_COND_MSG(frames == nullptr, vformat("There is no animation with name '%s'.", p_animation)); - ERR_FAIL_COND_MSG(frames->get_animation_names().find(p_animation) == -1, vformat("There is no animation with name '%s'.", p_animation)); + ERR_FAIL_COND_MSG(!frames->get_animation_names().has(p_animation), vformat("There is no animation with name '%s'.", p_animation)); if (animation == p_animation) { return; diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 1a6aaecaa8..7f2290bdc7 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -133,7 +133,7 @@ int Line2D::get_point_count() const { void Line2D::clear_points() { int count = _points.size(); if (count > 0) { - _points.resize(0); + _points.clear(); update(); } } diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp index 34f5830d8d..4bead978f1 100644 --- a/scene/2d/navigation_region_2d.cpp +++ b/scene/2d/navigation_region_2d.cpp @@ -299,7 +299,7 @@ void NavigationPolygon::make_polygons_from_outlines() { } polygons.clear(); - vertices.resize(0); + vertices.clear(); Map<Vector2, int> points; for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) { diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index fab420d593..852aaaab24 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -82,9 +82,11 @@ void Label::_shape() { Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label")); int width = (get_size().width - style->get_minimum_size().width); - if (dirty) { + if (dirty || font_dirty) { String lang = (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale(); - TS->shaped_text_clear(text_rid); + if (dirty) { + TS->shaped_text_clear(text_rid); + } if (text_direction == Control::TEXT_DIRECTION_INHERITED) { TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); } else { @@ -97,9 +99,17 @@ void Label::_shape() { if (visible_chars >= 0 && visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) { text = text.substr(0, visible_chars); } - TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang); + if (dirty) { + TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang); + } else { + int spans = TS->shaped_get_span_count(text_rid); + for (int i = 0; i < spans; i++) { + TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features); + } + } TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text)); dirty = false; + font_dirty = false; lines_dirty = true; } @@ -276,7 +286,7 @@ void Label::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); } - if (dirty || lines_dirty) { + if (dirty || font_dirty || lines_dirty) { _shape(); } @@ -521,7 +531,7 @@ void Label::_notification(int p_what) { } if (p_what == NOTIFICATION_THEME_CHANGED) { - dirty = true; + font_dirty = true; update(); } if (p_what == NOTIFICATION_RESIZED) { @@ -531,7 +541,7 @@ void Label::_notification(int p_what) { Size2 Label::get_minimum_size() const { // don't want to mutable everything - if (dirty || lines_dirty) { + if (dirty || font_dirty || lines_dirty) { const_cast<Label *>(this)->_shape(); } @@ -555,7 +565,7 @@ int Label::get_line_count() const { if (!is_inside_tree()) { return 1; } - if (dirty || lines_dirty) { + if (dirty || font_dirty || lines_dirty) { const_cast<Label *>(this)->_shape(); } @@ -630,7 +640,7 @@ void Label::set_text_direction(Control::TextDirection p_text_direction) { ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); if (text_direction != p_text_direction) { text_direction = p_text_direction; - dirty = true; + font_dirty = true; update(); } } @@ -638,7 +648,7 @@ void Label::set_text_direction(Control::TextDirection p_text_direction) { void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) { if (st_parser != p_parser) { st_parser = p_parser; - dirty = true; + font_dirty = true; update(); } } @@ -649,7 +659,7 @@ Control::StructuredTextParser Label::get_structured_text_bidi_override() const { void Label::set_structured_text_bidi_override_options(Array p_args) { st_args = p_args; - dirty = true; + font_dirty = true; update(); } @@ -663,7 +673,7 @@ Control::TextDirection Label::get_text_direction() const { void Label::clear_opentype_features() { opentype_features.clear(); - dirty = true; + font_dirty = true; update(); } @@ -671,7 +681,7 @@ void Label::set_opentype_feature(const String &p_name, int p_value) { int32_t tag = TS->name_to_tag(p_name); if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { opentype_features[tag] = p_value; - dirty = true; + font_dirty = true; update(); } } @@ -798,7 +808,7 @@ int Label::get_max_lines_visible() const { } int Label::get_total_character_count() const { - if (dirty || lines_dirty) { + if (dirty || font_dirty || lines_dirty) { const_cast<Label *>(this)->_shape(); } @@ -814,13 +824,13 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) { if (value == -1) { if (opentype_features.has(tag)) { opentype_features.erase(tag); - dirty = true; + font_dirty = true; update(); } } else { if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { opentype_features[tag] = value; - dirty = true; + font_dirty = true; update(); } } diff --git a/scene/gui/label.h b/scene/gui/label.h index 354e9c664d..0b931b3084 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -73,6 +73,7 @@ private: bool lines_dirty = true; bool dirty = true; + bool font_dirty = true; RID text_rid; Vector<RID> lines_rid; diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 151ae2f092..8f4c063c58 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -205,6 +205,49 @@ String RichTextLabel::_letters(int p_num, bool p_capitalize) const { return s; } +void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) { + ERR_FAIL_COND(p_frame == nullptr); + ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); + + Line &l = p_frame->lines.write[p_line]; + + RID t = l.text_buf->get_rid(); + int spans = TS->shaped_get_span_count(t); + for (int i = 0; i < spans; i++) { + ItemText *it = (ItemText *)(uint64_t)TS->shaped_get_span_meta(t, i); + if (it) { + Ref<Font> font = _find_font(it); + if (font.is_null()) { + font = p_base_font; + } + int font_size = _find_font_size(it); + if (font_size == -1) { + font_size = p_base_font_size; + } + Dictionary font_ftr = _find_font_features(it); + TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font_ftr); + } + } + + Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr; + for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) { + switch (it->type) { + case ITEM_TABLE: { + ItemTable *table = static_cast<ItemTable *>(it); + for (Item *E : table->subitems) { + ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames. + ItemFrame *frame = static_cast<ItemFrame *>(E); + for (int i = 0; i < frame->lines.size(); i++) { + _update_line_font(frame, i, p_base_font, p_base_font_size); + } + } + } break; + default: + break; + } + } +} + void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) { ERR_FAIL_COND(p_frame == nullptr); ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size()); @@ -449,7 +492,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } remaining_characters -= tx.length(); - l.text_buf->add_string(tx, font, font_size, font_ftr, lang); + l.text_buf->add_string(tx, font, font_size, font_ftr, lang, (uint64_t)it); text += tx; l.char_count += tx.length(); } break; @@ -1448,7 +1491,10 @@ void RichTextLabel::_notification(int p_what) { update(); } break; - case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_THEME_CHANGED: { + main->first_invalid_font_line = 0; //invalidate ALL + update(); + } break; case NOTIFICATION_ENTER_TREE: { if (!text.is_empty()) { set_text(text); @@ -1545,6 +1591,10 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const return get_default_cursor_shape(); //invalid } + if (main->first_invalid_font_line < main->lines.size()) { + return get_default_cursor_shape(); //invalid + } + if (main->first_resized_line < main->lines.size()) { return get_default_cursor_shape(); //invalid } @@ -1569,6 +1619,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { if (main->first_invalid_line < main->lines.size()) { return; } + if (main->first_invalid_font_line < main->lines.size()) { + return; + } if (main->first_resized_line < main->lines.size()) { return; } @@ -1732,6 +1785,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { if (main->first_invalid_line < main->lines.size()) { return; } + if (main->first_invalid_font_line < main->lines.size()) { + return; + } if (main->first_resized_line < main->lines.size()) { return; } @@ -2184,6 +2240,18 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) { void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { if (p_frame->first_invalid_line == p_frame->lines.size()) { + Ref<Font> base_font = get_theme_font(SNAME("normal_font")); + int base_font_size = get_theme_font_size(SNAME("normal_font_size")); + + // Update fonts. + if (p_frame->first_invalid_font_line != p_frame->lines.size()) { + for (int i = p_frame->first_invalid_font_line; i < p_frame->lines.size(); i++) { + _update_line_font(p_frame, i, base_font, base_font_size); + } + p_frame->first_resized_line = p_frame->first_invalid_font_line; + p_frame->first_invalid_font_line = p_frame->lines.size(); + } + if (p_frame->first_resized_line == p_frame->lines.size()) { return; } @@ -2191,9 +2259,6 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { // Resize lines without reshaping. Rect2 text_rect = _get_text_rect(); - Ref<Font> base_font = get_theme_font(SNAME("normal_font")); - int base_font_size = get_theme_font_size(SNAME("normal_font_size")); - for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) { _resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w); } @@ -2237,6 +2302,7 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) { p_frame->first_invalid_line = p_frame->lines.size(); p_frame->first_resized_line = p_frame->lines.size(); + p_frame->first_invalid_font_line = p_frame->lines.size(); updating_scroll = true; vscroll->set_max(total_height); @@ -4494,6 +4560,7 @@ RichTextLabel::RichTextLabel() { main->lines.write[0].from = main; main->first_invalid_line = 0; main->first_resized_line = 0; + main->first_invalid_font_line = 0; current_frame = main; vscroll = memnew(VScrollBar); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 70467e7e7c..2b24da4c37 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -129,6 +129,7 @@ private: Vector<Line> lines; int first_invalid_line = 0; + int first_invalid_font_line = 0; int first_resized_line = 0; ItemFrame *parent_frame = nullptr; @@ -414,6 +415,7 @@ private: void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset); void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width); + void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size); int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs); float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index e060d3b901..0ee4a6af4e 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -187,29 +187,44 @@ void TextEdit::Text::_calculate_max_line_width() { max_width = width; } -void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Array &p_bidi_override) { +void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_changed, const String &p_ime_text, const Array &p_bidi_override) { ERR_FAIL_INDEX(p_line, text.size()); if (font.is_null() || font_size <= 0) { return; // Not in tree? } - text.write[p_line].data_buf->clear(); + if (p_text_changed) { + text.write[p_line].data_buf->clear(); + } + text.write[p_line].data_buf->set_width(width); text.write[p_line].data_buf->set_direction((TextServer::Direction)direction); text.write[p_line].data_buf->set_preserve_control(draw_control_chars); if (p_ime_text.length() > 0) { - text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language); + if (p_text_changed) { + text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language); + } if (!p_bidi_override.is_empty()) { TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), p_bidi_override); } } else { - text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language); + if (p_text_changed) { + text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language); + } if (!text[p_line].bidi_override.is_empty()) { TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), text[p_line].bidi_override); } } + if (!p_text_changed) { + RID r = text.write[p_line].data_buf->get_rid(); + int spans = TS->shaped_get_span_count(r); + for (int i = 0; i < spans; i++) { + TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, opentype_features); + } + } + // Apply tab align. if (tab_size > 0) { Vector<float> tabs; @@ -266,6 +281,24 @@ void TextEdit::Text::invalidate_all_lines() { } } +void TextEdit::Text::invalidate_font() { + if (!is_dirty) { + return; + } + + max_width = -1; + line_height = -1; + + if (!font.is_null() && font_size > 0) { + font_height = font->get_height(font_size); + } + + for (int i = 0; i < text.size(); i++) { + invalidate_cache(i, -1, false); + } + is_dirty = false; +} + void TextEdit::Text::invalidate_all() { if (!is_dirty) { return; @@ -279,7 +312,7 @@ void TextEdit::Text::invalidate_all() { } for (int i = 0; i < text.size(); i++) { - invalidate_cache(i); + invalidate_cache(i, -1, true); } is_dirty = false; } @@ -294,7 +327,7 @@ void TextEdit::Text::clear() { line.gutters.resize(gutter_count); line.data = ""; text.insert(0, line); - invalidate_cache(0); + invalidate_cache(0, -1, true); } int TextEdit::Text::get_max_width() const { @@ -306,7 +339,7 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_o text.write[p_line].data = p_text; text.write[p_line].bidi_override = p_bidi_override; - invalidate_cache(p_line); + invalidate_cache(p_line, -1, true); } void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override) { @@ -331,7 +364,7 @@ void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector line.data = p_text[i]; line.bidi_override = p_bidi_override[i]; text.write[p_at + i] = line; - invalidate_cache(p_at + i); + invalidate_cache(p_at + i, -1, true); } } @@ -1446,9 +1479,11 @@ void TextEdit::_notification(int p_what) { DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id()); DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id()); } - ime_text = ""; - ime_selection = Point2(); - text.invalidate_cache(caret.line, caret.column, ime_text); + if (!ime_text.is_empty()) { + ime_text = ""; + ime_selection = Point2(); + text.invalidate_cache(caret.line, caret.column, true, ime_text); + } if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) { DisplayServer::get_singleton()->virtual_keyboard_hide(); @@ -1470,7 +1505,7 @@ void TextEdit::_notification(int p_what) { t = ime_text; } - text.invalidate_cache(caret.line, caret.column, t, structured_text_parser(st_parser, st_args, t)); + text.invalidate_cache(caret.line, caret.column, true, t, structured_text_parser(st_parser, st_args, t)); update(); } } break; @@ -2538,7 +2573,7 @@ void TextEdit::_update_caches() { text.set_draw_control_chars(draw_control_chars); text.set_font(font); text.set_font_size(font_size); - text.invalidate_all(); + text.invalidate_font(); _update_placeholder(); /* Syntax highlighting. */ @@ -2718,7 +2753,7 @@ void TextEdit::set_text_direction(Control::TextDirection p_text_direction) { dir = (TextServer::Direction)text_direction; } text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale()); - text.invalidate_all(); + text.invalidate_font(); _update_placeholder(); if (menu_dir) { @@ -2740,7 +2775,7 @@ void TextEdit::set_opentype_feature(const String &p_name, int p_value) { if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) { opentype_features[tag] = p_value; text.set_font_features(opentype_features); - text.invalidate_all(); + text.invalidate_font(); _update_placeholder(); update(); } @@ -2757,7 +2792,7 @@ int TextEdit::get_opentype_feature(const String &p_name) const { void TextEdit::clear_opentype_features() { opentype_features.clear(); text.set_font_features(opentype_features); - text.invalidate_all(); + text.invalidate_font(); _update_placeholder(); update(); } @@ -4852,7 +4887,7 @@ void TextEdit::set_draw_control_chars(bool p_enabled) { menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); } text.set_draw_control_chars(draw_control_chars); - text.invalidate_all(); + text.invalidate_font(); _update_placeholder(); update(); } @@ -5319,7 +5354,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) { if (opentype_features.has(tag)) { opentype_features.erase(tag); text.set_font_features(opentype_features); - text.invalidate_all(); + text.invalidate_font(); _update_placeholder(); update(); } @@ -5327,7 +5362,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) { if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) { opentype_features[tag] = value; text.set_font_features(opentype_features); - text.invalidate_all(); + text.invalidate_font(); _update_placeholder(); update(); } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 079890249e..83a63ae40a 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -210,7 +210,8 @@ private: int size() const { return text.size(); } void clear(); - void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Array &p_bidi_override = Array()); + void invalidate_cache(int p_line, int p_column = -1, bool p_text_changed = false, const String &p_ime_text = String(), const Array &p_bidi_override = Array()); + void invalidate_font(); void invalidate_all(); void invalidate_all_lines(); diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp index 65d210983e..dd66f566af 100644 --- a/scene/main/http_request.cpp +++ b/scene/main/http_request.cpp @@ -46,7 +46,7 @@ Error HTTPRequest::_parse_url(const String &p_url) { request_sent = false; got_response = false; body_len = -1; - body.resize(0); + body.clear(); downloaded.set(0); redirections = 0; @@ -202,7 +202,7 @@ void HTTPRequest::cancel_request() { file = nullptr; } client->close(); - body.resize(0); + body.clear(); got_response = false; response_code = -1; request_sent = false; @@ -220,7 +220,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { response_code = client->get_response_code(); List<String> rheaders; client->get_response_headers(&rheaders); - response_headers.resize(0); + response_headers.clear(); downloaded.set(0); for (const String &E : rheaders) { response_headers.push_back(E); @@ -260,7 +260,7 @@ bool HTTPRequest::_handle_response(bool *ret_value) { request_sent = false; got_response = false; body_len = -1; - body.resize(0); + body.clear(); downloaded.set(0); redirections = new_redirs; *ret_value = false; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 0c92dcae11..45f04b28b9 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -130,7 +130,7 @@ SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_nod E = group_map.insert(p_group, Group()); } - ERR_FAIL_COND_V_MSG(E->get().nodes.find(p_node) != -1, &E->get(), "Already in group: " + p_group + "."); + ERR_FAIL_COND_V_MSG(E->get().nodes.has(p_node), &E->get(), "Already in group: " + p_group + "."); E->get().nodes.push_back(p_node); //E->get().last_tree_version=0; E->get().changed = true; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 1244e0c028..bde68f0b3d 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -492,20 +492,26 @@ void Viewport::_notification(int p_what) { } #endif // _3D_DISABLED } break; + case NOTIFICATION_WM_MOUSE_ENTER: { + gui.mouse_in_window = true; + } break; case NOTIFICATION_WM_MOUSE_EXIT: { + gui.mouse_in_window = false; _drop_physics_mouseover(); - - // Unlike on loss of focus (NOTIFICATION_WM_WINDOW_FOCUS_OUT), do not - // drop the gui mouseover here, as a scrollbar may be dragged while the - // mouse is outside the window (without the window having lost focus). - // See bug #39634 + _drop_mouse_over(); + // When the mouse exits the window, we want to end mouse_over, but + // not mouse_focus, because, for example, we want to continue + // dragging a scrollbar even if the mouse has left the window. } break; case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { _drop_physics_mouseover(); - if (gui.mouse_focus && !gui.forced_mouse_focus) { _drop_mouse_focus(); } + // When the window focus changes, we want to end mouse_focus, but + // not the mouse_over. Note: The OS will trigger a separate mouse + // exit event if the change in focus results in the mouse exiting + // the window. } break; } } @@ -1447,8 +1453,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (mb.is_valid()) { gui.key_event_accepted = false; - Control *over = nullptr; - Point2 mpos = mb->get_position(); gui.last_mouse_pos = mpos; if (mb->is_pressed()) { @@ -1594,6 +1598,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { // it is different, rather than wait for it to be updated the next time the // mouse is moved, notify the control so that it can e.g. drop the highlight. // This code is duplicated from the mm.is_valid()-case further below. + Control *over = nullptr; if (gui.mouse_focus) { over = gui.mouse_focus; } else { @@ -1601,10 +1606,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } if (gui.mouse_focus_mask == MouseButton::NONE && over != gui.mouse_over) { - if (gui.mouse_over) { - _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT); - } - + _drop_mouse_over(); _gui_cancel_tooltip(); if (over) { @@ -1625,8 +1627,6 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { gui.last_mouse_pos = mpos; - Control *over = nullptr; - // Drag & drop. if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) { gui.drag_accum += mm->get_relative(); @@ -1674,29 +1674,23 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { } } - // These sections of code are reused in the mb.is_valid() case further up - // for the purpose of notifying controls about potential changes in focus - // when the mousebutton is released. + Control *over = nullptr; if (gui.mouse_focus) { over = gui.mouse_focus; - } else { + } else if (gui.mouse_in_window) { over = gui_find_control(mpos); } if (over != gui.mouse_over) { - if (gui.mouse_over) { - _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT); - } - + _drop_mouse_over(); _gui_cancel_tooltip(); if (over) { _gui_call_notification(over, Control::NOTIFICATION_MOUSE_ENTER); + gui.mouse_over = over; } } - gui.mouse_over = over; - DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)Input::get_singleton()->get_default_cursor_shape(); if (over) { @@ -2183,6 +2177,13 @@ void Viewport::_gui_accept_event() { } } +void Viewport::_drop_mouse_over() { + if (gui.mouse_over) { + _gui_call_notification(gui.mouse_over, Control::NOTIFICATION_MOUSE_EXIT); + gui.mouse_over = nullptr; + } +} + void Viewport::_drop_mouse_focus() { Control *c = gui.mouse_focus; MouseButton mask = gui.mouse_focus_mask; diff --git a/scene/main/viewport.h b/scene/main/viewport.h index a3127811f5..2541474cf3 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -335,6 +335,7 @@ private: // info used when this is a window bool forced_mouse_focus = false; //used for menu buttons + bool mouse_in_window = true; bool key_event_accepted = false; Control *mouse_focus = nullptr; Control *last_mouse_focus = nullptr; @@ -433,6 +434,7 @@ private: void _canvas_layer_add(CanvasLayer *p_canvas_layer); void _canvas_layer_remove(CanvasLayer *p_canvas_layer); + void _drop_mouse_over(); void _drop_mouse_focus(); void _drop_physics_mouseover(bool p_paused_only = false); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index d2cd76b796..6485c1ac77 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -661,8 +661,8 @@ void Curve2D::_bake() const { baked_cache_dirty = false; if (points.size() == 0) { - baked_point_cache.resize(0); - baked_dist_cache.resize(0); + baked_point_cache.clear(); + baked_dist_cache.clear(); return; } @@ -1164,10 +1164,10 @@ void Curve3D::_bake() const { baked_cache_dirty = false; if (points.size() == 0) { - baked_point_cache.resize(0); - baked_tilt_cache.resize(0); - baked_up_vector_cache.resize(0); - baked_dist_cache.resize(0); + baked_point_cache.clear(); + baked_tilt_cache.clear(); + baked_up_vector_cache.clear(); + baked_dist_cache.clear(); return; } @@ -1183,7 +1183,7 @@ void Curve3D::_bake() const { baked_up_vector_cache.resize(1); baked_up_vector_cache.set(0, Vector3(0, 1, 0)); } else { - baked_up_vector_cache.resize(0); + baked_up_vector_cache.clear(); } return; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index d2d96b1f06..441e84eccc 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -1475,12 +1475,12 @@ void ArrayMesh::add_blend_shape(const StringName &p_name) { StringName name = p_name; - if (blend_shapes.find(name) != -1) { + if (blend_shapes.has(name)) { int count = 2; do { name = String(p_name) + " " + itos(count); count++; - } while (blend_shapes.find(name) != -1); + } while (blend_shapes.has(name)); } blend_shapes.push_back(name); diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp index f3752053c0..c3b5bd3564 100644 --- a/scene/resources/text_line.cpp +++ b/scene/resources/text_line.cpp @@ -55,7 +55,7 @@ void TextLine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextLine::set_bidi_override); - ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL("")); + ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextLine::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextLine::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextLine::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); @@ -200,9 +200,9 @@ void TextLine::set_bidi_override(const Array &p_override) { dirty = true; } -bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { +bool TextLine::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { ERR_FAIL_COND_V(p_fonts.is_null(), false); - bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta); spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); dirty = true; diff --git a/scene/resources/text_line.h b/scene/resources/text_line.h index e68049ee45..c5762db0f2 100644 --- a/scene/resources/text_line.h +++ b/scene/resources/text_line.h @@ -86,7 +86,7 @@ public: void set_preserve_control(bool p_enabled); bool get_preserve_control() const; - bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); + bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()); bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp index c3bdef7b01..4d75874199 100644 --- a/scene/resources/text_paragraph.cpp +++ b/scene/resources/text_paragraph.cpp @@ -63,7 +63,7 @@ void TextParagraph::_bind_methods() { ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL("")); ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap); - ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL("")); + ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); @@ -344,9 +344,9 @@ void TextParagraph::clear_dropcap() { lines_dirty = true; } -bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { +bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { ERR_FAIL_COND_V(p_fonts.is_null(), false); - bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); + bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language, p_meta); spacing_top = p_fonts->get_spacing(TextServer::SPACING_TOP); spacing_bottom = p_fonts->get_spacing(TextServer::SPACING_BOTTOM); lines_dirty = true; diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h index 773cc1a858..8a8a53943b 100644 --- a/scene/resources/text_paragraph.h +++ b/scene/resources/text_paragraph.h @@ -102,7 +102,7 @@ public: bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); void clear_dropcap(); - bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); + bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()); bool add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1); bool resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER); diff --git a/servers/audio/effects/audio_effect_record.cpp b/servers/audio/effects/audio_effect_record.cpp index 569832b8a8..a5866bb380 100644 --- a/servers/audio/effects/audio_effect_record.cpp +++ b/servers/audio/effects/audio_effect_record.cpp @@ -112,7 +112,7 @@ void AudioEffectRecordInstance::init() { ring_buffer_read_pos = 0; //We start a new recording - recording_data.resize(0); //Clear data completely and reset length + recording_data.clear(); //Clear data completely and reset length is_recording = true; #ifdef NO_THREADS diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 7a50fd0e59..43a1812f89 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -925,7 +925,7 @@ void RendererSceneRenderRD::shadow_atlas_set_size(RID p_atlas, int p_size, bool } for (int i = 0; i < 4; i++) { //clear subdivisions - shadow_atlas->quadrants[i].shadows.resize(0); + shadow_atlas->quadrants[i].shadows.clear(); shadow_atlas->quadrants[i].shadows.resize(1 << shadow_atlas->quadrants[i].subdivision); } @@ -972,7 +972,7 @@ void RendererSceneRenderRD::shadow_atlas_set_quadrant_subdivision(RID p_atlas, i } } - shadow_atlas->quadrants[p_quadrant].shadows.resize(0); + shadow_atlas->quadrants[p_quadrant].shadows.clear(); shadow_atlas->quadrants[p_quadrant].shadows.resize(subdiv * subdiv); shadow_atlas->quadrants[p_quadrant].subdivision = subdiv; diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 2e63ac57d9..145c4f902e 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -6238,7 +6238,7 @@ void RendererStorageRD::skeleton_allocate_data(RID p_skeleton, int p_bones, bool if (skeleton->buffer.is_valid()) { RD::get_singleton()->free(skeleton->buffer); skeleton->buffer = RID(); - skeleton->data.resize(0); + skeleton->data.clear(); skeleton->uniform_set_mi = RID(); } diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index be96793ec4..5a84bace2d 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -822,7 +822,7 @@ void RendererViewport::viewport_set_active(RID p_viewport, bool p_active) { ERR_FAIL_COND(!viewport); if (p_active) { - ERR_FAIL_COND_MSG(active_viewports.find(viewport) != -1, "Can't make active a Viewport that is already active."); + ERR_FAIL_COND_MSG(active_viewports.has(viewport), "Can't make active a Viewport that is already active."); viewport->occlusion_buffer_dirty = true; active_viewports.push_back(viewport); } else { diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 59f9b0c808..cf77d2156f 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -7476,7 +7476,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct const String smode = String(mode); - if (shader->render_modes.find(mode) != -1) { + if (shader->render_modes.has(mode)) { _set_error(vformat(RTR("Duplicated render mode: '%s'."), smode)); return ERR_PARSE_ERROR; } @@ -7489,7 +7489,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct if (smode.begins_with(name)) { if (!info.options.is_empty()) { - if (info.options.find(smode.substr(name.length() + 1)) != -1) { + if (info.options.has(smode.substr(name.length() + 1))) { found = true; if (defined_modes.has(name)) { diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index d7e7960496..db1f589334 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -210,10 +210,14 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_shaped_text_set_preserve_control, "shaped", "enabled"); GDVIRTUAL_BIND(_shaped_text_get_preserve_control, "shaped"); - GDVIRTUAL_BIND(_shaped_text_add_string, "shaped", "text", "fonts", "size", "opentype_features", "language"); + GDVIRTUAL_BIND(_shaped_text_add_string, "shaped", "text", "fonts", "size", "opentype_features", "language", "meta"); GDVIRTUAL_BIND(_shaped_text_add_object, "shaped", "key", "size", "inline_align", "length"); GDVIRTUAL_BIND(_shaped_text_resize_object, "shaped", "key", "size", "inline_align"); + GDVIRTUAL_BIND(_shaped_get_span_count, "shaped"); + GDVIRTUAL_BIND(_shaped_get_span_meta, "shaped", "index"); + GDVIRTUAL_BIND(_shaped_set_span_update_font, "shaped", "index", "fonts", "size", "opentype_features"); + GDVIRTUAL_BIND(_shaped_text_substr, "shaped", "start", "length"); GDVIRTUAL_BIND(_shaped_text_get_parent, "shaped"); @@ -1018,13 +1022,13 @@ bool TextServerExtension::shaped_text_get_preserve_control(RID p_shaped) const { return false; } -bool TextServerExtension::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { +bool TextServerExtension::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) { bool ret; Array fonts; for (int i = 0; i < p_fonts.size(); i++) { fonts.push_back(p_fonts[i]); } - if (GDVIRTUAL_CALL(_shaped_text_add_string, p_shaped, p_text, fonts, p_size, p_opentype_features, p_language, ret)) { + if (GDVIRTUAL_CALL(_shaped_text_add_string, p_shaped, p_text, fonts, p_size, p_opentype_features, p_language, p_meta, ret)) { return ret; } return false; @@ -1046,6 +1050,30 @@ bool TextServerExtension::shaped_text_resize_object(RID p_shaped, Variant p_key, return false; } +int TextServerExtension::shaped_get_span_count(RID p_shaped) const { + int ret; + if (GDVIRTUAL_CALL(_shaped_get_span_count, p_shaped, ret)) { + return ret; + } + return 0; +} + +Variant TextServerExtension::shaped_get_span_meta(RID p_shaped, int p_index) const { + Variant ret; + if (GDVIRTUAL_CALL(_shaped_get_span_meta, p_shaped, p_index, ret)) { + return ret; + } + return false; +} + +void TextServerExtension::shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features) { + Array fonts; + for (int i = 0; i < p_fonts.size(); i++) { + fonts.push_back(p_fonts[i]); + } + GDVIRTUAL_CALL(_shaped_set_span_update_font, p_shaped, p_index, fonts, p_size, p_opentype_features); +} + RID TextServerExtension::shaped_text_substr(RID p_shaped, int p_start, int p_length) const { RID ret; if (GDVIRTUAL_CALL(_shaped_text_substr, p_shaped, p_start, p_length, ret)) { diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index 6e203f22ee..df1f1d7a70 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -343,13 +343,20 @@ public: GDVIRTUAL2(_shaped_text_set_preserve_control, RID, bool); GDVIRTUAL1RC(bool, _shaped_text_get_preserve_control, RID); - virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override; + virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override; virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override; virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override; - GDVIRTUAL6R(bool, _shaped_text_add_string, RID, const String &, const Array &, int, const Dictionary &, const String &); + GDVIRTUAL7R(bool, _shaped_text_add_string, RID, const String &, const Array &, int, const Dictionary &, const String &, const Variant &); GDVIRTUAL5R(bool, _shaped_text_add_object, RID, Variant, const Size2 &, InlineAlignment, int); GDVIRTUAL4R(bool, _shaped_text_resize_object, RID, Variant, const Size2 &, InlineAlignment); + virtual int shaped_get_span_count(RID p_shaped) const override; + virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const override; + virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) override; + GDVIRTUAL1RC(int, _shaped_get_span_count, RID); + GDVIRTUAL2RC(Variant, _shaped_get_span_meta, RID, int); + GDVIRTUAL5(_shaped_set_span_update_font, RID, int, const Array &, int, const Dictionary &); + virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override; virtual RID shaped_text_get_parent(RID p_shaped) const override; GDVIRTUAL3RC(RID, _shaped_text_substr, RID, int, int); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 17840584ee..b7cd39b9b2 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -363,10 +363,14 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("shaped_text_set_preserve_control", "shaped", "enabled"), &TextServer::shaped_text_set_preserve_control); ClassDB::bind_method(D_METHOD("shaped_text_get_preserve_control", "shaped"), &TextServer::shaped_text_get_preserve_control); - ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL("")); + ClassDB::bind_method(D_METHOD("shaped_text_add_string", "shaped", "text", "fonts", "size", "opentype_features", "language", "meta"), &TextServer::shaped_text_add_string, DEFVAL(Dictionary()), DEFVAL(""), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("shaped_text_add_object", "shaped", "key", "size", "inline_align", "length"), &TextServer::shaped_text_add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1)); ClassDB::bind_method(D_METHOD("shaped_text_resize_object", "shaped", "key", "size", "inline_align"), &TextServer::shaped_text_resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER)); + ClassDB::bind_method(D_METHOD("shaped_get_span_count", "shaped"), &TextServer::shaped_get_span_count); + ClassDB::bind_method(D_METHOD("shaped_get_span_meta", "shaped", "index"), &TextServer::shaped_get_span_meta); + ClassDB::bind_method(D_METHOD("shaped_set_span_update_font", "shaped", "index", "fonts", "size", "opentype_features"), &TextServer::shaped_set_span_update_font, DEFVAL(Dictionary())); + ClassDB::bind_method(D_METHOD("shaped_text_substr", "shaped", "start", "length"), &TextServer::shaped_text_substr); ClassDB::bind_method(D_METHOD("shaped_text_get_parent", "shaped"), &TextServer::shaped_text_get_parent); ClassDB::bind_method(D_METHOD("shaped_text_fit_to_width", "shaped", "width", "jst_flags"), &TextServer::shaped_text_fit_to_width, DEFVAL(JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA)); diff --git a/servers/text_server.h b/servers/text_server.h index 599cc3afd0..6724c02caf 100644 --- a/servers/text_server.h +++ b/servers/text_server.h @@ -155,20 +155,6 @@ protected: TextServer::Direction direction = DIRECTION_LTR; // Desired text direction. TextServer::Orientation orientation = ORIENTATION_HORIZONTAL; - struct Span { - int start = -1; - int end = -1; - - Vector<RID> fonts; - int font_size = 0; - - Variant embedded_key; - - String language; - Dictionary features; - }; - Vector<Span> spans; - struct EmbeddedObject { int pos = 0; InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER; @@ -387,10 +373,14 @@ public: virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) = 0; virtual bool shaped_text_get_preserve_control(RID p_shaped) const = 0; - virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") = 0; + virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) = 0; virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) = 0; virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) = 0; + virtual int shaped_get_span_count(RID p_shaped) const = 0; + virtual Variant shaped_get_span_meta(RID p_shaped, int p_index) const = 0; + virtual void shaped_set_span_update_font(RID p_shaped, int p_index, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary()) = 0; + virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const = 0; // Copy shaped substring (e.g. line break) without reshaping, but correctly reordered, preservers range. virtual RID shaped_text_get_parent(RID p_shaped) const = 0; |