text_paragraph.cpp 44 KB

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