text_paragraph.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177
  1. /**************************************************************************/
  2. /* text_paragraph.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "text_paragraph.h"
  31. #include "text_paragraph.compat.inc"
  32. void TextParagraph::_bind_methods() {
  33. ClassDB::bind_method(D_METHOD("clear"), &TextParagraph::clear);
  34. ClassDB::bind_method(D_METHOD("duplicate"), &TextParagraph::duplicate);
  35. ClassDB::bind_method(D_METHOD("set_direction", "direction"), &TextParagraph::set_direction);
  36. ClassDB::bind_method(D_METHOD("get_direction"), &TextParagraph::get_direction);
  37. ClassDB::bind_method(D_METHOD("get_inferred_direction"), &TextParagraph::get_inferred_direction);
  38. ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Left-to-right,Right-to-left"), "set_direction", "get_direction");
  39. // If compiling the editor with TextServerFallback only,
  40. // `--doctool` would change the default value to `TextServer::DIRECTION_LTR`.
  41. // Force it so that it's consistent regardless of the backend.
  42. ADD_PROPERTY_DEFAULT("direction", TextServer::DIRECTION_AUTO);
  43. ClassDB::bind_method(D_METHOD("set_custom_punctuation", "custom_punctuation"), &TextParagraph::set_custom_punctuation);
  44. ClassDB::bind_method(D_METHOD("get_custom_punctuation"), &TextParagraph::get_custom_punctuation);
  45. ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_punctuation"), "set_custom_punctuation", "get_custom_punctuation");
  46. ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextParagraph::set_orientation);
  47. ClassDB::bind_method(D_METHOD("get_orientation"), &TextParagraph::get_orientation);
  48. ADD_PROPERTY(PropertyInfo(Variant::INT, "orientation", PROPERTY_HINT_ENUM, "Horizontal,Orientation"), "set_orientation", "get_orientation");
  49. ClassDB::bind_method(D_METHOD("set_preserve_invalid", "enabled"), &TextParagraph::set_preserve_invalid);
  50. ClassDB::bind_method(D_METHOD("get_preserve_invalid"), &TextParagraph::get_preserve_invalid);
  51. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_invalid"), "set_preserve_invalid", "get_preserve_invalid");
  52. ClassDB::bind_method(D_METHOD("set_preserve_control", "enabled"), &TextParagraph::set_preserve_control);
  53. ClassDB::bind_method(D_METHOD("get_preserve_control"), &TextParagraph::get_preserve_control);
  54. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "preserve_control"), "set_preserve_control", "get_preserve_control");
  55. ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::set_bidi_override);
  56. ClassDB::bind_method(D_METHOD("set_dropcap", "text", "font", "font_size", "dropcap_margins", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(""));
  57. ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
  58. ClassDB::bind_method(D_METHOD("add_string", "text", "font", "font_size", "language", "meta"), &TextParagraph::add_string, DEFVAL(""), DEFVAL(Variant()));
  59. ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length", "baseline"), &TextParagraph::add_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(1), DEFVAL(0.0));
  60. ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align", "baseline"), &TextParagraph::resize_object, DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(0.0));
  61. ClassDB::bind_method(D_METHOD("has_object", "key"), &TextParagraph::has_object);
  62. ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &TextParagraph::set_alignment);
  63. ClassDB::bind_method(D_METHOD("get_alignment"), &TextParagraph::get_alignment);
  64. ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_alignment", "get_alignment");
  65. ClassDB::bind_method(D_METHOD("tab_align", "tab_stops"), &TextParagraph::tab_align);
  66. ClassDB::bind_method(D_METHOD("set_break_flags", "flags"), &TextParagraph::set_break_flags);
  67. ClassDB::bind_method(D_METHOD("get_break_flags"), &TextParagraph::get_break_flags);
  68. ADD_PROPERTY(PropertyInfo(Variant::INT, "break_flags", PROPERTY_HINT_FLAGS, "Mandatory,Word Bound,Grapheme Bound,Adaptive,Trim Spaces"), "set_break_flags", "get_break_flags");
  69. ClassDB::bind_method(D_METHOD("set_justification_flags", "flags"), &TextParagraph::set_justification_flags);
  70. ClassDB::bind_method(D_METHOD("get_justification_flags"), &TextParagraph::get_justification_flags);
  71. ADD_PROPERTY(PropertyInfo(Variant::INT, "justification_flags", PROPERTY_HINT_FLAGS, "Kashida Justification:1,Word Justification:2,Trim Edge Spaces After Justification:4,Justify Only After Last Tab:8,Constrain Ellipsis:16,Skip Last Line:32,Skip Last Line With Visible Characters:64,Do Not Skip Single Line:128"), "set_justification_flags", "get_justification_flags");
  72. ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &TextParagraph::set_text_overrun_behavior);
  73. ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &TextParagraph::get_text_overrun_behavior);
  74. ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis (6+ Characters),Word Ellipsis (6+ Characters),Ellipsis (Always),Word Ellipsis (Always)"), "set_text_overrun_behavior", "get_text_overrun_behavior");
  75. ClassDB::bind_method(D_METHOD("set_ellipsis_char", "char"), &TextParagraph::set_ellipsis_char);
  76. ClassDB::bind_method(D_METHOD("get_ellipsis_char"), &TextParagraph::get_ellipsis_char);
  77. ADD_PROPERTY(PropertyInfo(Variant::STRING, "ellipsis_char"), "set_ellipsis_char", "get_ellipsis_char");
  78. ClassDB::bind_method(D_METHOD("set_width", "width"), &TextParagraph::set_width);
  79. ClassDB::bind_method(D_METHOD("get_width"), &TextParagraph::get_width);
  80. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width"), "set_width", "get_width");
  81. ClassDB::bind_method(D_METHOD("get_non_wrapped_size"), &TextParagraph::get_non_wrapped_size);
  82. ClassDB::bind_method(D_METHOD("get_size"), &TextParagraph::get_size);
  83. ClassDB::bind_method(D_METHOD("get_rid"), &TextParagraph::get_rid);
  84. ClassDB::bind_method(D_METHOD("get_line_rid", "line"), &TextParagraph::get_line_rid);
  85. ClassDB::bind_method(D_METHOD("get_dropcap_rid"), &TextParagraph::get_dropcap_rid);
  86. ClassDB::bind_method(D_METHOD("get_range"), &TextParagraph::get_range);
  87. ClassDB::bind_method(D_METHOD("get_line_count"), &TextParagraph::get_line_count);
  88. ClassDB::bind_method(D_METHOD("set_max_lines_visible", "max_lines_visible"), &TextParagraph::set_max_lines_visible);
  89. ClassDB::bind_method(D_METHOD("get_max_lines_visible"), &TextParagraph::get_max_lines_visible);
  90. ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible"), "set_max_lines_visible", "get_max_lines_visible");
  91. ClassDB::bind_method(D_METHOD("set_line_spacing", "line_spacing"), &TextParagraph::set_line_spacing);
  92. ClassDB::bind_method(D_METHOD("get_line_spacing"), &TextParagraph::get_line_spacing);
  93. ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing"), "set_line_spacing", "get_line_spacing");
  94. ClassDB::bind_method(D_METHOD("get_line_objects", "line"), &TextParagraph::get_line_objects);
  95. ClassDB::bind_method(D_METHOD("get_line_object_rect", "line", "key"), &TextParagraph::get_line_object_rect);
  96. ClassDB::bind_method(D_METHOD("get_line_size", "line"), &TextParagraph::get_line_size);
  97. ClassDB::bind_method(D_METHOD("get_line_range", "line"), &TextParagraph::get_line_range);
  98. ClassDB::bind_method(D_METHOD("get_line_ascent", "line"), &TextParagraph::get_line_ascent);
  99. ClassDB::bind_method(D_METHOD("get_line_descent", "line"), &TextParagraph::get_line_descent);
  100. ClassDB::bind_method(D_METHOD("get_line_width", "line"), &TextParagraph::get_line_width);
  101. ClassDB::bind_method(D_METHOD("get_line_underline_position", "line"), &TextParagraph::get_line_underline_position);
  102. ClassDB::bind_method(D_METHOD("get_line_underline_thickness", "line"), &TextParagraph::get_line_underline_thickness);
  103. ClassDB::bind_method(D_METHOD("get_dropcap_size"), &TextParagraph::get_dropcap_size);
  104. ClassDB::bind_method(D_METHOD("get_dropcap_lines"), &TextParagraph::get_dropcap_lines);
  105. ClassDB::bind_method(D_METHOD("draw", "canvas", "pos", "color", "dc_color", "oversampling"), &TextParagraph::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0));
  106. ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color", "dc_color", "oversampling"), &TextParagraph::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0));
  107. ClassDB::bind_method(D_METHOD("draw_line", "canvas", "pos", "line", "color", "oversampling"), &TextParagraph::draw_line, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0));
  108. ClassDB::bind_method(D_METHOD("draw_line_outline", "canvas", "pos", "line", "outline_size", "color", "oversampling"), &TextParagraph::draw_line_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0));
  109. ClassDB::bind_method(D_METHOD("draw_dropcap", "canvas", "pos", "color", "oversampling"), &TextParagraph::draw_dropcap, DEFVAL(Color(1, 1, 1)), DEFVAL(0.0));
  110. ClassDB::bind_method(D_METHOD("draw_dropcap_outline", "canvas", "pos", "outline_size", "color", "oversampling"), &TextParagraph::draw_dropcap_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(0.0));
  111. ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextParagraph::hit_test);
  112. }
  113. void TextParagraph::_shape_lines() const {
  114. // When a shaped text is invalidated by an external source, we want to reshape it.
  115. if (!TS->shaped_text_is_ready(rid) || !TS->shaped_text_is_ready(dropcap_rid)) {
  116. lines_dirty = true;
  117. }
  118. for (const RID &line_rid : lines_rid) {
  119. if (!TS->shaped_text_is_ready(line_rid)) {
  120. lines_dirty = true;
  121. break;
  122. }
  123. }
  124. if (lines_dirty) {
  125. for (const RID &line_rid : lines_rid) {
  126. TS->free_rid(line_rid);
  127. }
  128. lines_rid.clear();
  129. if (!tab_stops.is_empty()) {
  130. TS->shaped_text_tab_align(rid, tab_stops);
  131. }
  132. float h_offset = 0.f;
  133. float v_offset = 0.f;
  134. int start = 0;
  135. dropcap_lines = 0;
  136. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  137. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  138. v_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  139. } else {
  140. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  141. v_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  142. }
  143. Size2i range = TS->shaped_text_get_range(rid);
  144. if (h_offset > 0) {
  145. // Dropcap, flow around.
  146. PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, brk_flags);
  147. for (int i = 0; i < line_breaks.size(); i = i + 2) {
  148. RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
  149. float h = (TS->shaped_text_get_orientation(line) == TextServer::ORIENTATION_HORIZONTAL) ? TS->shaped_text_get_size(line).y : TS->shaped_text_get_size(line).x;
  150. h += line_spacing;
  151. if (!tab_stops.is_empty()) {
  152. TS->shaped_text_tab_align(line, tab_stops);
  153. }
  154. start = (i < line_breaks.size() - 2) ? line_breaks[i + 2] : range.y;
  155. lines_rid.push_back(line);
  156. if (v_offset < h) {
  157. break;
  158. }
  159. dropcap_lines++;
  160. v_offset -= h;
  161. }
  162. }
  163. // Use fixed for the rest of lines.
  164. if (start == 0 || start < range.y) {
  165. PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, brk_flags);
  166. for (int i = 0; i < line_breaks.size(); i = i + 2) {
  167. RID line = TS->shaped_text_substr(rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
  168. if (!tab_stops.is_empty()) {
  169. TS->shaped_text_tab_align(line, tab_stops);
  170. }
  171. lines_rid.push_back(line);
  172. }
  173. }
  174. BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::get_overrun_flags_from_behavior(overrun_behavior);
  175. bool autowrap_enabled = brk_flags.has_flag(TextServer::BREAK_WORD_BOUND) || brk_flags.has_flag(TextServer::BREAK_GRAPHEME_BOUND);
  176. // Fill after min_size calculation.
  177. if (autowrap_enabled) {
  178. int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size();
  179. bool lines_hidden = visible_lines > 0 && visible_lines < (int)lines_rid.size();
  180. if (lines_hidden) {
  181. overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS);
  182. }
  183. if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
  184. int jst_to_line = visible_lines;
  185. if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) {
  186. jst_to_line = lines_rid.size();
  187. } else {
  188. if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) {
  189. jst_to_line = visible_lines - 1;
  190. }
  191. if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) {
  192. for (int i = visible_lines - 1; i >= 0; i--) {
  193. if (TS->shaped_text_has_visible_chars(lines_rid[i])) {
  194. jst_to_line = i;
  195. break;
  196. }
  197. }
  198. }
  199. }
  200. for (int i = 0; i < (int)lines_rid.size(); i++) {
  201. float line_w = (i <= dropcap_lines) ? (width - h_offset) : width;
  202. if (i < jst_to_line) {
  203. TS->shaped_text_fit_to_width(lines_rid[i], line_w, jst_flags);
  204. } else if (i == (visible_lines - 1)) {
  205. TS->shaped_text_set_custom_ellipsis(lines_rid[visible_lines - 1], (el_char.length() > 0) ? el_char[0] : 0x2026);
  206. TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], line_w, overrun_flags);
  207. }
  208. }
  209. } else if (lines_hidden) {
  210. TS->shaped_text_set_custom_ellipsis(lines_rid[visible_lines - 1], (el_char.length() > 0) ? el_char[0] : 0x2026);
  211. TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], (visible_lines - 1 <= dropcap_lines) ? (width - h_offset) : width, overrun_flags);
  212. }
  213. } else {
  214. // Autowrap disabled.
  215. int jst_to_line = lines_rid.size();
  216. if (lines_rid.size() == 1 && jst_flags.has_flag(TextServer::JUSTIFICATION_DO_NOT_SKIP_SINGLE_LINE)) {
  217. jst_to_line = lines_rid.size();
  218. } else {
  219. if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE)) {
  220. jst_to_line = lines_rid.size() - 1;
  221. }
  222. if (jst_flags.has_flag(TextServer::JUSTIFICATION_SKIP_LAST_LINE_WITH_VISIBLE_CHARS)) {
  223. for (int i = lines_rid.size() - 1; i >= 0; i--) {
  224. if (TS->shaped_text_has_visible_chars(lines_rid[i])) {
  225. jst_to_line = i;
  226. break;
  227. }
  228. }
  229. }
  230. }
  231. for (int i = 0; i < (int)lines_rid.size(); i++) {
  232. float line_w = (i <= dropcap_lines) ? (width - h_offset) : width;
  233. if (i < jst_to_line && alignment == HORIZONTAL_ALIGNMENT_FILL) {
  234. TS->shaped_text_fit_to_width(lines_rid[i], line_w, jst_flags);
  235. overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE);
  236. TS->shaped_text_set_custom_ellipsis(lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026);
  237. TS->shaped_text_overrun_trim_to_width(lines_rid[i], line_w, overrun_flags);
  238. TS->shaped_text_fit_to_width(lines_rid[i], line_w, jst_flags | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS);
  239. } else {
  240. TS->shaped_text_set_custom_ellipsis(lines_rid[i], (el_char.length() > 0) ? el_char[0] : 0x2026);
  241. TS->shaped_text_overrun_trim_to_width(lines_rid[i], line_w, overrun_flags);
  242. }
  243. }
  244. }
  245. lines_dirty = false;
  246. }
  247. }
  248. RID TextParagraph::get_rid() const {
  249. return rid;
  250. }
  251. RID TextParagraph::get_line_rid(int p_line) const {
  252. _THREAD_SAFE_METHOD_
  253. _shape_lines();
  254. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), RID());
  255. return lines_rid[p_line];
  256. }
  257. RID TextParagraph::get_dropcap_rid() const {
  258. return dropcap_rid;
  259. }
  260. void TextParagraph::clear() {
  261. _THREAD_SAFE_METHOD_
  262. for (const RID &line_rid : lines_rid) {
  263. TS->free_rid(line_rid);
  264. }
  265. lines_rid.clear();
  266. TS->shaped_text_clear(rid);
  267. TS->shaped_text_clear(dropcap_rid);
  268. }
  269. Ref<TextParagraph> TextParagraph::duplicate() const {
  270. Ref<TextParagraph> copy;
  271. copy.instantiate();
  272. if (dropcap_rid.is_valid()) {
  273. TS->free_rid(copy->dropcap_rid);
  274. copy->dropcap_rid = TS->shaped_text_duplicate(dropcap_rid);
  275. }
  276. copy->dropcap_lines = dropcap_lines;
  277. copy->dropcap_margins = dropcap_margins;
  278. if (rid.is_valid()) {
  279. TS->free_rid(copy->rid);
  280. copy->rid = TS->shaped_text_duplicate(rid);
  281. }
  282. copy->lines_dirty = true;
  283. copy->line_spacing = line_spacing;
  284. copy->width = width;
  285. copy->max_lines_visible = max_lines_visible;
  286. copy->brk_flags = brk_flags;
  287. copy->jst_flags = jst_flags;
  288. copy->el_char = el_char;
  289. copy->overrun_behavior = overrun_behavior;
  290. copy->alignment = alignment;
  291. copy->tab_stops = tab_stops;
  292. return copy;
  293. }
  294. void TextParagraph::set_preserve_invalid(bool p_enabled) {
  295. _THREAD_SAFE_METHOD_
  296. TS->shaped_text_set_preserve_invalid(rid, p_enabled);
  297. TS->shaped_text_set_preserve_invalid(dropcap_rid, p_enabled);
  298. lines_dirty = true;
  299. }
  300. bool TextParagraph::get_preserve_invalid() const {
  301. _THREAD_SAFE_METHOD_
  302. return TS->shaped_text_get_preserve_invalid(rid);
  303. }
  304. void TextParagraph::set_preserve_control(bool p_enabled) {
  305. _THREAD_SAFE_METHOD_
  306. TS->shaped_text_set_preserve_control(rid, p_enabled);
  307. TS->shaped_text_set_preserve_control(dropcap_rid, p_enabled);
  308. lines_dirty = true;
  309. }
  310. bool TextParagraph::get_preserve_control() const {
  311. _THREAD_SAFE_METHOD_
  312. return TS->shaped_text_get_preserve_control(rid);
  313. }
  314. void TextParagraph::set_direction(TextServer::Direction p_direction) {
  315. _THREAD_SAFE_METHOD_
  316. TS->shaped_text_set_direction(rid, p_direction);
  317. TS->shaped_text_set_direction(dropcap_rid, p_direction);
  318. lines_dirty = true;
  319. }
  320. TextServer::Direction TextParagraph::get_direction() const {
  321. _THREAD_SAFE_METHOD_
  322. _shape_lines();
  323. return TS->shaped_text_get_direction(rid);
  324. }
  325. TextServer::Direction TextParagraph::get_inferred_direction() const {
  326. _THREAD_SAFE_METHOD_
  327. const_cast<TextParagraph *>(this)->_shape_lines();
  328. return TS->shaped_text_get_inferred_direction(rid);
  329. }
  330. void TextParagraph::set_custom_punctuation(const String &p_punct) {
  331. _THREAD_SAFE_METHOD_
  332. TS->shaped_text_set_custom_punctuation(rid, p_punct);
  333. lines_dirty = true;
  334. }
  335. String TextParagraph::get_custom_punctuation() const {
  336. _THREAD_SAFE_METHOD_
  337. return TS->shaped_text_get_custom_punctuation(rid);
  338. }
  339. void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
  340. _THREAD_SAFE_METHOD_
  341. TS->shaped_text_set_orientation(rid, p_orientation);
  342. TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
  343. lines_dirty = true;
  344. }
  345. TextServer::Orientation TextParagraph::get_orientation() const {
  346. _THREAD_SAFE_METHOD_
  347. _shape_lines();
  348. return TS->shaped_text_get_orientation(rid);
  349. }
  350. bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_font, int p_font_size, const Rect2 &p_dropcap_margins, const String &p_language) {
  351. _THREAD_SAFE_METHOD_
  352. ERR_FAIL_COND_V(p_font.is_null(), false);
  353. TS->shaped_text_clear(dropcap_rid);
  354. dropcap_margins = p_dropcap_margins;
  355. bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language);
  356. lines_dirty = true;
  357. return res;
  358. }
  359. void TextParagraph::clear_dropcap() {
  360. _THREAD_SAFE_METHOD_
  361. dropcap_margins = Rect2();
  362. TS->shaped_text_clear(dropcap_rid);
  363. lines_dirty = true;
  364. }
  365. bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, const Variant &p_meta) {
  366. _THREAD_SAFE_METHOD_
  367. ERR_FAIL_COND_V(p_font.is_null(), false);
  368. bool res = TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language, p_meta);
  369. lines_dirty = true;
  370. return res;
  371. }
  372. void TextParagraph::set_bidi_override(const Array &p_override) {
  373. _THREAD_SAFE_METHOD_
  374. TS->shaped_text_set_bidi_override(rid, p_override);
  375. lines_dirty = true;
  376. }
  377. bool TextParagraph::add_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length, float p_baseline) {
  378. _THREAD_SAFE_METHOD_
  379. bool res = TS->shaped_text_add_object(rid, p_key, p_size, p_inline_align, p_length, p_baseline);
  380. lines_dirty = true;
  381. return res;
  382. }
  383. bool TextParagraph::resize_object(Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, float p_baseline) {
  384. _THREAD_SAFE_METHOD_
  385. bool res = TS->shaped_text_resize_object(rid, p_key, p_size, p_inline_align, p_baseline);
  386. lines_dirty = true;
  387. return res;
  388. }
  389. bool TextParagraph::has_object(Variant p_key) const {
  390. _THREAD_SAFE_METHOD_
  391. return TS->shaped_text_has_object(rid, p_key);
  392. }
  393. void TextParagraph::set_alignment(HorizontalAlignment p_alignment) {
  394. _THREAD_SAFE_METHOD_
  395. if (alignment != p_alignment) {
  396. if (alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
  397. alignment = p_alignment;
  398. lines_dirty = true;
  399. } else {
  400. alignment = p_alignment;
  401. }
  402. }
  403. }
  404. HorizontalAlignment TextParagraph::get_alignment() const {
  405. return alignment;
  406. }
  407. void TextParagraph::tab_align(const Vector<float> &p_tab_stops) {
  408. _THREAD_SAFE_METHOD_
  409. tab_stops = p_tab_stops;
  410. lines_dirty = true;
  411. }
  412. void TextParagraph::set_justification_flags(BitField<TextServer::JustificationFlag> p_flags) {
  413. _THREAD_SAFE_METHOD_
  414. if (jst_flags != p_flags) {
  415. jst_flags = p_flags;
  416. lines_dirty = true;
  417. }
  418. }
  419. BitField<TextServer::JustificationFlag> TextParagraph::get_justification_flags() const {
  420. return jst_flags;
  421. }
  422. void TextParagraph::set_break_flags(BitField<TextServer::LineBreakFlag> p_flags) {
  423. _THREAD_SAFE_METHOD_
  424. if (brk_flags != p_flags) {
  425. brk_flags = p_flags;
  426. lines_dirty = true;
  427. }
  428. }
  429. BitField<TextServer::LineBreakFlag> TextParagraph::get_break_flags() const {
  430. return brk_flags;
  431. }
  432. void TextParagraph::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) {
  433. _THREAD_SAFE_METHOD_
  434. if (overrun_behavior != p_behavior) {
  435. overrun_behavior = p_behavior;
  436. lines_dirty = true;
  437. }
  438. }
  439. TextServer::OverrunBehavior TextParagraph::get_text_overrun_behavior() const {
  440. return overrun_behavior;
  441. }
  442. void TextParagraph::set_ellipsis_char(const String &p_char) {
  443. String c = p_char;
  444. if (c.length() > 1) {
  445. WARN_PRINT("Ellipsis must be exactly one character long (" + itos(c.length()) + " characters given).");
  446. c = c.left(1);
  447. }
  448. if (el_char == c) {
  449. return;
  450. }
  451. el_char = c;
  452. lines_dirty = true;
  453. }
  454. String TextParagraph::get_ellipsis_char() const {
  455. return el_char;
  456. }
  457. void TextParagraph::set_width(float p_width) {
  458. _THREAD_SAFE_METHOD_
  459. if (width != p_width) {
  460. width = p_width;
  461. lines_dirty = true;
  462. }
  463. }
  464. float TextParagraph::get_width() const {
  465. return width;
  466. }
  467. Size2 TextParagraph::get_non_wrapped_size() const {
  468. _THREAD_SAFE_METHOD_
  469. _shape_lines();
  470. return TS->shaped_text_get_size(rid);
  471. }
  472. Size2 TextParagraph::get_size() const {
  473. _THREAD_SAFE_METHOD_
  474. _shape_lines();
  475. float h_offset = 0.f;
  476. float v_offset = 0.f;
  477. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  478. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  479. v_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  480. } else {
  481. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  482. v_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  483. }
  484. Size2 size;
  485. int visible_lines = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size();
  486. for (int i = 0; i < visible_lines; i++) {
  487. Size2 lsize = TS->shaped_text_get_size(lines_rid[i]);
  488. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  489. if (h_offset > 0 && i <= dropcap_lines) {
  490. lsize.x += h_offset;
  491. }
  492. size.x = MAX(size.x, lsize.x);
  493. size.y += lsize.y;
  494. if (i != visible_lines - 1) {
  495. size.y += line_spacing;
  496. }
  497. } else {
  498. if (h_offset > 0 && i <= dropcap_lines) {
  499. lsize.y += h_offset;
  500. }
  501. size.x += lsize.x;
  502. size.y = MAX(size.y, lsize.y);
  503. if (i != visible_lines - 1) {
  504. size.x += line_spacing;
  505. }
  506. }
  507. }
  508. if (h_offset > 0) {
  509. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  510. size.y = MAX(size.y, v_offset);
  511. } else {
  512. size.x = MAX(size.x, v_offset);
  513. }
  514. }
  515. return size;
  516. }
  517. Vector2i TextParagraph::get_range() const {
  518. _THREAD_SAFE_METHOD_
  519. return TS->shaped_text_get_range(rid);
  520. }
  521. int TextParagraph::get_line_count() const {
  522. _THREAD_SAFE_METHOD_
  523. _shape_lines();
  524. return (int)lines_rid.size();
  525. }
  526. void TextParagraph::set_max_lines_visible(int p_lines) {
  527. _THREAD_SAFE_METHOD_
  528. if (p_lines != max_lines_visible) {
  529. max_lines_visible = p_lines;
  530. lines_dirty = true;
  531. }
  532. }
  533. int TextParagraph::get_max_lines_visible() const {
  534. return max_lines_visible;
  535. }
  536. void TextParagraph::set_line_spacing(float p_spacing) {
  537. _THREAD_SAFE_METHOD_
  538. if (line_spacing != p_spacing) {
  539. line_spacing = p_spacing;
  540. lines_dirty = true;
  541. }
  542. }
  543. float TextParagraph::get_line_spacing() const {
  544. return line_spacing;
  545. }
  546. Array TextParagraph::get_line_objects(int p_line) const {
  547. _THREAD_SAFE_METHOD_
  548. _shape_lines();
  549. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Array());
  550. return TS->shaped_text_get_objects(lines_rid[p_line]);
  551. }
  552. Rect2 TextParagraph::get_line_object_rect(int p_line, Variant p_key) const {
  553. _THREAD_SAFE_METHOD_
  554. _shape_lines();
  555. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Rect2());
  556. Vector2 ofs;
  557. float h_offset = 0.f;
  558. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  559. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  560. } else {
  561. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  562. }
  563. for (int i = 0; i <= p_line; i++) {
  564. float l_width = width;
  565. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  566. ofs.x = 0.f;
  567. ofs.y += TS->shaped_text_get_ascent(lines_rid[i]);
  568. if (i <= dropcap_lines) {
  569. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  570. ofs.x -= h_offset;
  571. }
  572. l_width -= h_offset;
  573. }
  574. } else {
  575. ofs.y = 0.f;
  576. ofs.x += TS->shaped_text_get_ascent(lines_rid[i]);
  577. if (i <= dropcap_lines) {
  578. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  579. ofs.y -= h_offset;
  580. }
  581. l_width -= h_offset;
  582. }
  583. }
  584. float length = TS->shaped_text_get_width(lines_rid[i]);
  585. if (width > 0) {
  586. switch (alignment) {
  587. case HORIZONTAL_ALIGNMENT_FILL:
  588. if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  589. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  590. ofs.x += l_width - length;
  591. } else {
  592. ofs.y += l_width - length;
  593. }
  594. }
  595. break;
  596. case HORIZONTAL_ALIGNMENT_LEFT:
  597. break;
  598. case HORIZONTAL_ALIGNMENT_CENTER: {
  599. if (length <= l_width) {
  600. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  601. ofs.x += Math::floor((l_width - length) / 2.0);
  602. } else {
  603. ofs.y += Math::floor((l_width - length) / 2.0);
  604. }
  605. } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  606. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  607. ofs.x += l_width - length;
  608. } else {
  609. ofs.y += l_width - length;
  610. }
  611. }
  612. } break;
  613. case HORIZONTAL_ALIGNMENT_RIGHT: {
  614. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  615. ofs.x += l_width - length;
  616. } else {
  617. ofs.y += l_width - length;
  618. }
  619. } break;
  620. }
  621. }
  622. if (i != p_line) {
  623. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  624. ofs.x = 0.f;
  625. ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
  626. } else {
  627. ofs.y = 0.f;
  628. ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
  629. }
  630. }
  631. }
  632. Rect2 rect = TS->shaped_text_get_object_rect(lines_rid[p_line], p_key);
  633. rect.position += ofs;
  634. return rect;
  635. }
  636. Size2 TextParagraph::get_line_size(int p_line) const {
  637. _THREAD_SAFE_METHOD_
  638. _shape_lines();
  639. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Size2());
  640. return TS->shaped_text_get_size(lines_rid[p_line]);
  641. }
  642. Vector2i TextParagraph::get_line_range(int p_line) const {
  643. _THREAD_SAFE_METHOD_
  644. _shape_lines();
  645. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), Vector2i());
  646. return TS->shaped_text_get_range(lines_rid[p_line]);
  647. }
  648. float TextParagraph::get_line_ascent(int p_line) const {
  649. _THREAD_SAFE_METHOD_
  650. _shape_lines();
  651. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  652. return TS->shaped_text_get_ascent(lines_rid[p_line]);
  653. }
  654. float TextParagraph::get_line_descent(int p_line) const {
  655. _THREAD_SAFE_METHOD_
  656. _shape_lines();
  657. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  658. return TS->shaped_text_get_descent(lines_rid[p_line]);
  659. }
  660. float TextParagraph::get_line_width(int p_line) const {
  661. _THREAD_SAFE_METHOD_
  662. _shape_lines();
  663. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  664. return TS->shaped_text_get_width(lines_rid[p_line]);
  665. }
  666. float TextParagraph::get_line_underline_position(int p_line) const {
  667. _THREAD_SAFE_METHOD_
  668. _shape_lines();
  669. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  670. return TS->shaped_text_get_underline_position(lines_rid[p_line]);
  671. }
  672. float TextParagraph::get_line_underline_thickness(int p_line) const {
  673. _THREAD_SAFE_METHOD_
  674. _shape_lines();
  675. ERR_FAIL_COND_V(p_line < 0 || p_line >= (int)lines_rid.size(), 0.f);
  676. return TS->shaped_text_get_underline_thickness(lines_rid[p_line]);
  677. }
  678. Size2 TextParagraph::get_dropcap_size() const {
  679. _THREAD_SAFE_METHOD_
  680. return TS->shaped_text_get_size(dropcap_rid) + dropcap_margins.size + dropcap_margins.position;
  681. }
  682. int TextParagraph::get_dropcap_lines() const {
  683. return dropcap_lines;
  684. }
  685. void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color, const Color &p_dc_color, float p_oversampling) const {
  686. _THREAD_SAFE_METHOD_
  687. _shape_lines();
  688. Vector2 ofs = p_pos;
  689. float h_offset = 0.f;
  690. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  691. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  692. } else {
  693. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  694. }
  695. if (h_offset > 0) {
  696. // Draw dropcap.
  697. Vector2 dc_off = ofs;
  698. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
  699. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  700. dc_off.x += width - h_offset;
  701. } else {
  702. dc_off.y += width - h_offset;
  703. }
  704. }
  705. TS->shaped_text_draw(dropcap_rid, p_canvas, dc_off + Vector2(0, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.size.y + dropcap_margins.position.y / 2), -1, -1, p_dc_color, p_oversampling);
  706. }
  707. int lines_visible = (max_lines_visible >= 0) ? MIN(max_lines_visible, (int)lines_rid.size()) : (int)lines_rid.size();
  708. for (int i = 0; i < lines_visible; i++) {
  709. float l_width = width;
  710. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  711. ofs.x = p_pos.x;
  712. ofs.y += TS->shaped_text_get_ascent(lines_rid[i]);
  713. if (i <= dropcap_lines) {
  714. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  715. ofs.x -= h_offset;
  716. }
  717. l_width -= h_offset;
  718. }
  719. } else {
  720. ofs.y = p_pos.y;
  721. ofs.x += TS->shaped_text_get_ascent(lines_rid[i]);
  722. if (i <= dropcap_lines) {
  723. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  724. ofs.y -= h_offset;
  725. }
  726. l_width -= h_offset;
  727. }
  728. }
  729. float line_width = TS->shaped_text_get_width(lines_rid[i]);
  730. if (width > 0) {
  731. switch (alignment) {
  732. case HORIZONTAL_ALIGNMENT_FILL:
  733. if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  734. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  735. ofs.x += l_width - line_width;
  736. } else {
  737. ofs.y += l_width - line_width;
  738. }
  739. }
  740. break;
  741. case HORIZONTAL_ALIGNMENT_LEFT:
  742. break;
  743. case HORIZONTAL_ALIGNMENT_CENTER: {
  744. if (line_width <= l_width) {
  745. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  746. ofs.x += Math::floor((l_width - line_width) / 2.0);
  747. } else {
  748. ofs.y += Math::floor((l_width - line_width) / 2.0);
  749. }
  750. } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  751. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  752. ofs.x += l_width - line_width;
  753. } else {
  754. ofs.y += l_width - line_width;
  755. }
  756. }
  757. } break;
  758. case HORIZONTAL_ALIGNMENT_RIGHT: {
  759. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  760. ofs.x += l_width - line_width;
  761. } else {
  762. ofs.y += l_width - line_width;
  763. }
  764. } break;
  765. }
  766. }
  767. float clip_l;
  768. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  769. clip_l = MAX(0, p_pos.x - ofs.x);
  770. } else {
  771. clip_l = MAX(0, p_pos.y - ofs.y);
  772. }
  773. TS->shaped_text_draw(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color, p_oversampling);
  774. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  775. ofs.x = p_pos.x;
  776. ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
  777. } else {
  778. ofs.y = p_pos.y;
  779. ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
  780. }
  781. }
  782. }
  783. void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, const Color &p_dc_color, float p_oversampling) const {
  784. _THREAD_SAFE_METHOD_
  785. _shape_lines();
  786. Vector2 ofs = p_pos;
  787. float h_offset = 0.f;
  788. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  789. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  790. } else {
  791. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  792. }
  793. if (h_offset > 0) {
  794. // Draw dropcap.
  795. Vector2 dc_off = ofs;
  796. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
  797. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  798. dc_off.x += width - h_offset;
  799. } else {
  800. dc_off.y += width - h_offset;
  801. }
  802. }
  803. TS->shaped_text_draw_outline(dropcap_rid, p_canvas, dc_off + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_dc_color, p_oversampling);
  804. }
  805. for (int i = 0; i < (int)lines_rid.size(); i++) {
  806. float l_width = width;
  807. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  808. ofs.x = p_pos.x;
  809. ofs.y += TS->shaped_text_get_ascent(lines_rid[i]);
  810. if (i <= dropcap_lines) {
  811. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  812. ofs.x -= h_offset;
  813. }
  814. l_width -= h_offset;
  815. }
  816. } else {
  817. ofs.y = p_pos.y;
  818. ofs.x += TS->shaped_text_get_ascent(lines_rid[i]);
  819. if (i <= dropcap_lines) {
  820. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
  821. ofs.y -= h_offset;
  822. }
  823. l_width -= h_offset;
  824. }
  825. }
  826. float length = TS->shaped_text_get_width(lines_rid[i]);
  827. if (width > 0) {
  828. switch (alignment) {
  829. case HORIZONTAL_ALIGNMENT_FILL:
  830. if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  831. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  832. ofs.x += l_width - length;
  833. } else {
  834. ofs.y += l_width - length;
  835. }
  836. }
  837. break;
  838. case HORIZONTAL_ALIGNMENT_LEFT:
  839. break;
  840. case HORIZONTAL_ALIGNMENT_CENTER: {
  841. if (length <= l_width) {
  842. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  843. ofs.x += Math::floor((l_width - length) / 2.0);
  844. } else {
  845. ofs.y += Math::floor((l_width - length) / 2.0);
  846. }
  847. } else if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
  848. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  849. ofs.x += l_width - length;
  850. } else {
  851. ofs.y += l_width - length;
  852. }
  853. }
  854. } break;
  855. case HORIZONTAL_ALIGNMENT_RIGHT: {
  856. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  857. ofs.x += l_width - length;
  858. } else {
  859. ofs.y += l_width - length;
  860. }
  861. } break;
  862. }
  863. }
  864. float clip_l;
  865. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  866. clip_l = MAX(0, p_pos.x - ofs.x);
  867. } else {
  868. clip_l = MAX(0, p_pos.y - ofs.y);
  869. }
  870. TS->shaped_text_draw_outline(lines_rid[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color, p_oversampling);
  871. if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
  872. ofs.x = p_pos.x;
  873. ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
  874. } else {
  875. ofs.y = p_pos.y;
  876. ofs.x += TS->shaped_text_get_descent(lines_rid[i]) + line_spacing;
  877. }
  878. }
  879. }
  880. int TextParagraph::hit_test(const Point2 &p_coords) const {
  881. _THREAD_SAFE_METHOD_
  882. _shape_lines();
  883. Vector2 ofs;
  884. if (TS->shaped_text_get_orientation(rid) == TextServer::ORIENTATION_HORIZONTAL) {
  885. if (ofs.y < 0) {
  886. return 0;
  887. }
  888. } else {
  889. if (ofs.x < 0) {
  890. return 0;
  891. }
  892. }
  893. for (const RID &line_rid : lines_rid) {
  894. if (TS->shaped_text_get_orientation(line_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  895. if ((p_coords.y >= ofs.y) && (p_coords.y <= ofs.y + TS->shaped_text_get_size(line_rid).y)) {
  896. return TS->shaped_text_hit_test_position(line_rid, p_coords.x);
  897. }
  898. ofs.y += TS->shaped_text_get_size(line_rid).y + line_spacing;
  899. } else {
  900. if ((p_coords.x >= ofs.x) && (p_coords.x <= ofs.x + TS->shaped_text_get_size(line_rid).x)) {
  901. return TS->shaped_text_hit_test_position(line_rid, p_coords.y);
  902. }
  903. ofs.y += TS->shaped_text_get_size(line_rid).x + line_spacing;
  904. }
  905. }
  906. return TS->shaped_text_get_range(rid).y;
  907. }
  908. bool TextParagraph::is_dirty() {
  909. return lines_dirty;
  910. }
  911. void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color, float p_oversampling) const {
  912. _THREAD_SAFE_METHOD_
  913. Vector2 ofs = p_pos;
  914. float h_offset = 0.f;
  915. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  916. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  917. } else {
  918. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  919. }
  920. if (h_offset > 0) {
  921. // Draw dropcap.
  922. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
  923. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  924. ofs.x += width - h_offset;
  925. } else {
  926. ofs.y += width - h_offset;
  927. }
  928. }
  929. TS->shaped_text_draw(dropcap_rid, p_canvas, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_color, p_oversampling);
  930. }
  931. }
  932. void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, float p_oversampling) const {
  933. _THREAD_SAFE_METHOD_
  934. Vector2 ofs = p_pos;
  935. float h_offset = 0.f;
  936. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  937. h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
  938. } else {
  939. h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
  940. }
  941. if (h_offset > 0) {
  942. // Draw dropcap.
  943. if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
  944. if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
  945. ofs.x += width - h_offset;
  946. } else {
  947. ofs.y += width - h_offset;
  948. }
  949. }
  950. TS->shaped_text_draw_outline(dropcap_rid, p_canvas, ofs + Vector2(dropcap_margins.position.x, TS->shaped_text_get_ascent(dropcap_rid) + dropcap_margins.position.y), -1, -1, p_outline_size, p_color, p_oversampling);
  951. }
  952. }
  953. void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color, float p_oversampling) const {
  954. _THREAD_SAFE_METHOD_
  955. _shape_lines();
  956. ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size());
  957. Vector2 ofs = p_pos;
  958. if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
  959. ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]);
  960. } else {
  961. ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]);
  962. }
  963. return TS->shaped_text_draw(lines_rid[p_line], p_canvas, ofs, -1, -1, p_color, p_oversampling);
  964. }
  965. void TextParagraph::draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size, const Color &p_color, float p_oversampling) const {
  966. _THREAD_SAFE_METHOD_
  967. _shape_lines();
  968. ERR_FAIL_COND(p_line < 0 || p_line >= (int)lines_rid.size());
  969. Vector2 ofs = p_pos;
  970. if (TS->shaped_text_get_orientation(lines_rid[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
  971. ofs.y += TS->shaped_text_get_ascent(lines_rid[p_line]);
  972. } else {
  973. ofs.x += TS->shaped_text_get_ascent(lines_rid[p_line]);
  974. }
  975. return TS->shaped_text_draw_outline(lines_rid[p_line], p_canvas, ofs, -1, -1, p_outline_size, p_color, p_oversampling);
  976. }
  977. TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_font, int p_font_size, const String &p_language, float p_width, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
  978. rid = TS->create_shaped_text(p_direction, p_orientation);
  979. if (p_font.is_valid()) {
  980. TS->shaped_text_add_string(rid, p_text, p_font->get_rids(), p_font_size, p_font->get_opentype_features(), p_language);
  981. }
  982. width = p_width;
  983. }
  984. TextParagraph::TextParagraph() {
  985. rid = TS->create_shaped_text();
  986. dropcap_rid = TS->create_shaped_text();
  987. }
  988. TextParagraph::~TextParagraph() {
  989. for (const RID &line_rid : lines_rid) {
  990. TS->free_rid(line_rid);
  991. }
  992. lines_rid.clear();
  993. TS->free_rid(rid);
  994. TS->free_rid(dropcap_rid);
  995. }