Merge pull request #43691 from bruvzg/ctl_dropcap

[Complex Text Layouts] Add drop-cap support to RTL.
This commit is contained in:
Rémi Verschelde 2020-12-29 09:14:46 +01:00 committed by GitHub
commit 652bdd573e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 460 additions and 26 deletions

View file

@ -164,6 +164,27 @@
Adds a [code][color][/code] tag to the tag stack. Adds a [code][color][/code] tag to the tag stack.
</description> </description>
</method> </method>
<method name="push_dropcap">
<return type="void">
</return>
<argument index="0" name="string" type="String">
</argument>
<argument index="1" name="font" type="Font">
</argument>
<argument index="2" name="size" type="int">
</argument>
<argument index="3" name="dropcap_margins" type="Rect2" default="Rect2( 0, 0, 0, 0 )">
</argument>
<argument index="4" name="color" type="Color" default="Color( 1, 1, 1, 1 )">
</argument>
<argument index="5" name="outline_size" type="int" default="0">
</argument>
<argument index="6" name="outline_color" type="Color" default="Color( 0, 0, 0, 0 )">
</argument>
<description>
Adds a [code][dropcap][/code] tag to the tag stack. Drop cap (dropped capital) is a decorative element at the beginning of a paragraph that is larger than the rest of the text.
</description>
</method>
<method name="push_font"> <method name="push_font">
<return type="void"> <return type="void">
</return> </return>
@ -525,10 +546,12 @@
</constant> </constant>
<constant name="ITEM_RAINBOW" value="20" enum="ItemType"> <constant name="ITEM_RAINBOW" value="20" enum="ItemType">
</constant> </constant>
<constant name="ITEM_CUSTOMFX" value="22" enum="ItemType">
</constant>
<constant name="ITEM_META" value="21" enum="ItemType"> <constant name="ITEM_META" value="21" enum="ItemType">
</constant> </constant>
<constant name="ITEM_DROPCAP" value="22" enum="ItemType">
</constant>
<constant name="ITEM_CUSTOMFX" value="23" enum="ItemType">
</constant>
</constants> </constants>
<theme_items> <theme_items>
<theme_item name="bold_font" type="Font"> <theme_item name="bold_font" type="Font">

View file

@ -49,6 +49,13 @@
Clears text paragraph (removes text and inline objects). Clears text paragraph (removes text and inline objects).
</description> </description>
</method> </method>
<method name="clear_dropcap">
<return type="void">
</return>
<description>
Removes dropcap.
</description>
</method>
<method name="draw" qualifiers="const"> <method name="draw" qualifiers="const">
<return type="void"> <return type="void">
</return> </return>
@ -58,8 +65,38 @@
</argument> </argument>
<argument index="2" name="color" type="Color" default="Color( 1, 1, 1, 1 )"> <argument index="2" name="color" type="Color" default="Color( 1, 1, 1, 1 )">
</argument> </argument>
<argument index="3" name="dc_color" type="Color" default="Color( 1, 1, 1, 1 )">
</argument>
<description> <description>
Draw text into a canvas item at a given position, with [code]color[/code]. [code]pos[/code] specifies the top left corner of the bounding box. Draw all lines of the text and drop cap into a canvas item at a given position, with [code]color[/code]. [code]pos[/code] specifies the top left corner of the bounding box.
</description>
</method>
<method name="draw_dropcap" qualifiers="const">
<return type="void">
</return>
<argument index="0" name="canvas" type="RID">
</argument>
<argument index="1" name="pos" type="Vector2">
</argument>
<argument index="2" name="color" type="Color" default="Color( 1, 1, 1, 1 )">
</argument>
<description>
Draw drop cap into a canvas item at a given position, with [code]color[/code]. [code]pos[/code] specifies the top left corner of the bounding box.
</description>
</method>
<method name="draw_dropcap_outline" qualifiers="const">
<return type="void">
</return>
<argument index="0" name="canvas" type="RID">
</argument>
<argument index="1" name="pos" type="Vector2">
</argument>
<argument index="2" name="outline_size" type="int" default="1">
</argument>
<argument index="3" name="color" type="Color" default="Color( 1, 1, 1, 1 )">
</argument>
<description>
Draw drop cap outline into a canvas item at a given position, with [code]color[/code]. [code]pos[/code] specifies the top left corner of the bounding box.
</description> </description>
</method> </method>
<method name="draw_line" qualifiers="const"> <method name="draw_line" qualifiers="const">
@ -99,14 +136,37 @@
</return> </return>
<argument index="0" name="canvas" type="RID"> <argument index="0" name="canvas" type="RID">
</argument> </argument>
<argument index="1" name="outline_size" type="Vector2"> <argument index="1" name="pos" type="Vector2">
</argument> </argument>
<argument index="2" name="color" type="int" default="1"> <argument index="2" name="outline_size" type="int" default="1">
</argument> </argument>
<argument index="3" name="arg3" type="Color" default="Color( 1, 1, 1, 1 )"> <argument index="3" name="color" type="Color" default="Color( 1, 1, 1, 1 )">
</argument>
<argument index="4" name="dc_color" type="Color" default="Color( 1, 1, 1, 1 )">
</argument> </argument>
<description> <description>
Draw outline of the text into a canvas item at a given position, with [code]color[/code]. [code]pos[/code] specifies the top left corner of the bounding box. Draw outilines of all lines of the text and drop cap into a canvas item at a given position, with [code]color[/code]. [code]pos[/code] specifies the top left corner of the bounding box.
</description>
</method>
<method name="get_dropcap_lines" qualifiers="const">
<return type="int">
</return>
<description>
Returns number of lines used by dropcap.
</description>
</method>
<method name="get_dropcap_rid" qualifiers="const">
<return type="RID">
</return>
<description>
Return drop cap text buffer RID.
</description>
</method>
<method name="get_dropcap_size" qualifiers="const">
<return type="Vector2">
</return>
<description>
Returns drop cap bounding box size.
</description> </description>
</method> </method>
<method name="get_line_ascent" qualifiers="const"> <method name="get_line_ascent" qualifiers="const">
@ -261,6 +321,26 @@
Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately. Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately.
</description> </description>
</method> </method>
<method name="set_dropcap">
<return type="bool">
</return>
<argument index="0" name="text" type="String">
</argument>
<argument index="1" name="fonts" type="Font">
</argument>
<argument index="2" name="size" type="int">
</argument>
<argument index="3" name="dropcap_margins" type="Rect2" default="Rect2( 0, 0, 0, 0 )">
</argument>
<argument index="4" name="opentype_features" type="Dictionary" default="{
}">
</argument>
<argument index="5" name="language" type="String" default="&quot;&quot;">
</argument>
<description>
Sets drop cap, overrides previously set drop cap. Drop cap (dropped capital) is a decorative element at the beginning of a paragraph that is larger than the rest of the text.
</description>
</method>
<method name="tab_align"> <method name="tab_align">
<return type="void"> <return type="void">
</return> </return>

View file

@ -404,6 +404,18 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
break; break;
} }
switch (it->type) { switch (it->type) {
case ITEM_DROPCAP: {
// Add dropcap.
const ItemDropcap *dc = (ItemDropcap *)it;
if (dc != nullptr) {
l.text_buf->set_dropcap(dc->text, dc->font, dc->font_size, dc->dropcap_margins);
l.dc_color = dc->color;
l.dc_ol_size = dc->ol_size;
l.dc_ol_color = dc->ol_color;
} else {
l.text_buf->clear_dropcap();
}
} break;
case ITEM_NEWLINE: { case ITEM_NEWLINE: {
Ref<Font> font = _find_font(it); Ref<Font> font = _find_font(it);
if (font.is_null()) { if (font.is_null()) {
@ -679,6 +691,14 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
} }
} }
// Draw dropcap.
int dc_lines = l.text_buf->get_dropcap_lines();
float h_off = l.text_buf->get_dropcap_size().x;
if (l.dc_ol_size > 0) {
l.text_buf->draw_dropcap_outline(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_ol_size, l.dc_ol_color);
}
l.text_buf->draw_dropcap(ci, p_ofs + ((rtl) ? Vector2() : Vector2(l.offset.x, 0)), l.dc_color);
// Draw text. // Draw text.
for (int line = 0; line < l.text_buf->get_line_count(); line++) { for (int line = 0; line < l.text_buf->get_line_count(); line++) {
RID rid = l.text_buf->get_line_rid(line); RID rid = l.text_buf->get_line_rid(line);
@ -718,6 +738,14 @@ void RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_
} break; } break;
} }
if (line <= dc_lines) {
if (rtl) {
off.x -= h_off;
} else {
off.x += h_off;
}
}
//draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS //draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS
off.y += TS->shaped_text_get_ascent(rid); off.y += TS->shaped_text_get_ascent(rid);
@ -1742,6 +1770,19 @@ Dictionary RichTextLabel::_find_font_features(Item *p_item) {
return Dictionary(); return Dictionary();
} }
RichTextLabel::ItemDropcap *RichTextLabel::_find_dc_item(Item *p_item) {
Item *item = p_item;
while (item) {
if (item->type == ITEM_DROPCAP) {
return static_cast<ItemDropcap *>(item);
}
item = item->parent;
}
return nullptr;
}
RichTextLabel::ItemList *RichTextLabel::_find_list_item(Item *p_item) { RichTextLabel::ItemList *RichTextLabel::_find_list_item(Item *p_item) {
Item *item = p_item; Item *item = p_item;
@ -2316,6 +2357,24 @@ bool RichTextLabel::remove_line(const int p_line) {
return true; return true;
} }
void RichTextLabel::push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins, const Color &p_color, int p_ol_size, const Color &p_ol_color) {
ERR_FAIL_COND(current->type == ITEM_TABLE);
ERR_FAIL_COND(p_string.empty());
ERR_FAIL_COND(p_font.is_null());
ERR_FAIL_COND(p_size <= 0);
ItemDropcap *item = memnew(ItemDropcap);
item->text = p_string;
item->font = p_font;
item->font_size = p_size;
item->color = p_color;
item->ol_size = p_ol_size;
item->ol_color = p_ol_color;
item->dropcap_margins = p_dropcap_margins;
_add_item(item, false);
}
void RichTextLabel::push_font(const Ref<Font> &p_font) { void RichTextLabel::push_font(const Ref<Font> &p_font) {
ERR_FAIL_COND(current->type == ITEM_TABLE); ERR_FAIL_COND(current->type == ITEM_TABLE);
ERR_FAIL_COND(p_font.is_null()); ERR_FAIL_COND(p_font.is_null());
@ -2765,7 +2824,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
tag_stack.pop_front(); tag_stack.pop_front();
pos = brk_end + 1; pos = brk_end + 1;
if (tag != "/img") { if (tag != "/img" && tag != "/dropcap") {
pop(); pop();
} }
@ -3041,6 +3100,54 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
push_meta(url); push_meta(url);
pos = brk_end + 1; pos = brk_end + 1;
tag_stack.push_front("url"); tag_stack.push_front("url");
} else if (tag.begins_with("dropcap")) {
Vector<String> subtag = tag.substr(5, tag.length()).split(" ");
Ref<Font> f = get_theme_font("normal_font");
int fs = get_theme_font_size("normal_font_size") * 3;
Color color = get_theme_color("default_color");
Color outline_color = get_theme_color("outline_color");
int outline_size = get_theme_constant("outline_size");
Rect2 dropcap_margins = Rect2();
for (int i = 0; i < subtag.size(); i++) {
Vector<String> subtag_a = subtag[i].split("=");
if (subtag_a.size() == 2) {
if (subtag_a[0] == "font" || subtag_a[0] == "f") {
String fnt = subtag_a[1];
Ref<Font> font = ResourceLoader::load(fnt, "Font");
if (font.is_valid()) {
f = font;
}
} else if (subtag_a[0] == "font_size") {
fs = subtag_a[1].to_int();
} else if (subtag_a[0] == "margins") {
Vector<String> subtag_b = subtag_a[1].split(",");
if (subtag_b.size() == 4) {
dropcap_margins.position.x = subtag_b[0].to_float();
dropcap_margins.position.y = subtag_b[1].to_float();
dropcap_margins.size.x = subtag_b[2].to_float();
dropcap_margins.size.y = subtag_b[3].to_float();
}
} else if (subtag_a[0] == "outline_size") {
outline_size = subtag_a[1].to_int();
} else if (subtag_a[0] == "color") {
color = _get_color_from_string(subtag_a[1], color);
} else if (subtag_a[0] == "outline_color") {
outline_color = _get_color_from_string(subtag_a[1], outline_color);
}
}
}
int end = p_bbcode.find("[", brk_end);
if (end == -1) {
end = p_bbcode.length();
}
String txt = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
push_dropcap(txt, f, fs, dropcap_margins, color, outline_size, outline_color);
pos = end;
tag_stack.push_front(bbcode_name);
} else if (tag.begins_with("img")) { } else if (tag.begins_with("img")) {
VAlign align = VALIGN_TOP; VAlign align = VALIGN_TOP;
if (tag.begins_with("img=")) { if (tag.begins_with("img=")) {
@ -3679,6 +3786,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline); ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline);
ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough); ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough);
ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(VALIGN_TOP)); ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(VALIGN_TOP));
ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0)));
ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand); ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand);
ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color); ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color);
ClassDB::bind_method(D_METHOD("set_cell_border_color", "color"), &RichTextLabel::set_cell_border_color); ClassDB::bind_method(D_METHOD("set_cell_border_color", "color"), &RichTextLabel::set_cell_border_color);
@ -3814,8 +3922,9 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(ITEM_WAVE); BIND_ENUM_CONSTANT(ITEM_WAVE);
BIND_ENUM_CONSTANT(ITEM_TORNADO); BIND_ENUM_CONSTANT(ITEM_TORNADO);
BIND_ENUM_CONSTANT(ITEM_RAINBOW); BIND_ENUM_CONSTANT(ITEM_RAINBOW);
BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
BIND_ENUM_CONSTANT(ITEM_META); BIND_ENUM_CONSTANT(ITEM_META);
BIND_ENUM_CONSTANT(ITEM_DROPCAP);
BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
} }
void RichTextLabel::set_visible_characters(int p_visible) { void RichTextLabel::set_visible_characters(int p_visible) {

View file

@ -76,6 +76,7 @@ public:
ITEM_TORNADO, ITEM_TORNADO,
ITEM_RAINBOW, ITEM_RAINBOW,
ITEM_META, ITEM_META,
ITEM_DROPCAP,
ITEM_CUSTOMFX ITEM_CUSTOMFX
}; };
@ -89,6 +90,9 @@ private:
Item *from = nullptr; Item *from = nullptr;
Ref<TextParagraph> text_buf; Ref<TextParagraph> text_buf;
Color dc_color;
int dc_ol_size = 0;
Color dc_ol_color;
Vector2 offset; Vector2 offset;
int char_offset = 0; int char_offset = 0;
@ -140,6 +144,17 @@ private:
ItemText() { type = ITEM_TEXT; } ItemText() { type = ITEM_TEXT; }
}; };
struct ItemDropcap : public Item {
String text;
Ref<Font> font;
int font_size;
Color color;
int ol_size;
Color ol_color;
Rect2 dropcap_margins;
ItemDropcap() { type = ITEM_DROPCAP; }
};
struct ItemImage : public Item { struct ItemImage : public Item {
Ref<Texture2D> image; Ref<Texture2D> image;
VAlign inline_align = VALIGN_TOP; VAlign inline_align = VALIGN_TOP;
@ -391,6 +406,7 @@ private:
Dictionary _find_font_features(Item *p_item); Dictionary _find_font_features(Item *p_item);
int _find_outline_size(Item *p_item); int _find_outline_size(Item *p_item);
ItemList *_find_list_item(Item *p_item); ItemList *_find_list_item(Item *p_item);
ItemDropcap *_find_dc_item(Item *p_item);
int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list); int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list);
int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size); int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size);
Align _find_align(Item *p_item); Align _find_align(Item *p_item);
@ -435,6 +451,7 @@ public:
void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), VAlign p_align = VALIGN_TOP); void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), VAlign p_align = VALIGN_TOP);
void add_newline(); void add_newline();
bool remove_line(const int p_line); bool remove_line(const int p_line);
void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0));
void push_font(const Ref<Font> &p_font); void push_font(const Ref<Font> &p_font);
void push_font_size(int p_font_size); void push_font_size(int p_font_size);
void push_font_features(const Dictionary &p_features); void push_font_features(const Dictionary &p_features);

View file

@ -55,6 +55,9 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::_set_bidi_override); ClassDB::bind_method(D_METHOD("set_bidi_override", "override"), &TextParagraph::_set_bidi_override);
ClassDB::bind_method(D_METHOD("set_dropcap", "text", "fonts", "size", "dropcap_margins", "opentype_features", "language"), &TextParagraph::set_dropcap, DEFVAL(Rect2()), DEFVAL(Dictionary()), DEFVAL(""));
ClassDB::bind_method(D_METHOD("clear_dropcap"), &TextParagraph::clear_dropcap);
ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL("")); ClassDB::bind_method(D_METHOD("add_string", "text", "fonts", "size", "opentype_features", "language"), &TextParagraph::add_string, DEFVAL(Dictionary()), DEFVAL(""));
ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1)); ClassDB::bind_method(D_METHOD("add_object", "key", "size", "inline_align", "length"), &TextParagraph::add_object, DEFVAL(VALIGN_CENTER), DEFVAL(1));
ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(VALIGN_CENTER)); ClassDB::bind_method(D_METHOD("resize_object", "key", "size", "inline_align"), &TextParagraph::resize_object, DEFVAL(VALIGN_CENTER));
@ -81,6 +84,7 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rid"), &TextParagraph::get_rid); ClassDB::bind_method(D_METHOD("get_rid"), &TextParagraph::get_rid);
ClassDB::bind_method(D_METHOD("get_line_rid", "line"), &TextParagraph::get_line_rid); ClassDB::bind_method(D_METHOD("get_line_rid", "line"), &TextParagraph::get_line_rid);
ClassDB::bind_method(D_METHOD("get_dropcap_rid"), &TextParagraph::get_dropcap_rid);
ClassDB::bind_method(D_METHOD("get_line_count"), &TextParagraph::get_line_count); ClassDB::bind_method(D_METHOD("get_line_count"), &TextParagraph::get_line_count);
@ -94,12 +98,18 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_underline_position", "line"), &TextParagraph::get_line_underline_position); ClassDB::bind_method(D_METHOD("get_line_underline_position", "line"), &TextParagraph::get_line_underline_position);
ClassDB::bind_method(D_METHOD("get_line_underline_thickness", "line"), &TextParagraph::get_line_underline_thickness); ClassDB::bind_method(D_METHOD("get_line_underline_thickness", "line"), &TextParagraph::get_line_underline_thickness);
ClassDB::bind_method(D_METHOD("draw", "canvas", "pos", "color"), &TextParagraph::draw, DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("get_dropcap_size"), &TextParagraph::get_dropcap_size);
ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "outline_size", "color"), &TextParagraph::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("get_dropcap_lines"), &TextParagraph::get_dropcap_lines);
ClassDB::bind_method(D_METHOD("draw", "canvas", "pos", "color", "dc_color"), &TextParagraph::draw, DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_outline", "canvas", "pos", "outline_size", "color", "dc_color"), &TextParagraph::draw_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)), DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_line", "canvas", "pos", "line", "color"), &TextParagraph::draw_line, DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_line", "canvas", "pos", "line", "color"), &TextParagraph::draw_line, DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_line_outline", "canvas", "pos", "line", "outline_size", "color"), &TextParagraph::draw_line_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1))); ClassDB::bind_method(D_METHOD("draw_line_outline", "canvas", "pos", "line", "outline_size", "color"), &TextParagraph::draw_line_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_dropcap", "canvas", "pos", "color"), &TextParagraph::draw_dropcap, DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_dropcap_outline", "canvas", "pos", "outline_size", "color"), &TextParagraph::draw_dropcap_outline, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextParagraph::hit_test); ClassDB::bind_method(D_METHOD("hit_test", "coords"), &TextParagraph::hit_test);
} }
@ -114,7 +124,43 @@ void TextParagraph::_shape_lines() {
TS->shaped_text_tab_align(rid, tab_stops); TS->shaped_text_tab_align(rid, tab_stops);
} }
Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width, 0, flags); float h_offset = 0.f;
float v_offset = 0.f;
int start = 0;
dropcap_lines = 0;
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
v_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
} else {
h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
v_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
}
if (h_offset > 0) {
// Dropcap, flow around.
Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width - h_offset, 0, flags);
for (int i = 0; i < line_breaks.size(); i++) {
RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x);
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;
if (v_offset < h) {
TS->free(line);
break;
}
if (!tab_stops.empty()) {
TS->shaped_text_tab_align(line, tab_stops);
}
if (align == HALIGN_FILL && (line_breaks.size() == 1 || i < line_breaks.size() - 1)) {
TS->shaped_text_fit_to_width(line, width - h_offset, flags);
}
dropcap_lines++;
v_offset -= h;
start = line_breaks[i].y;
lines.push_back(line);
}
}
// Use fixed for the rest of lines.
Vector<Vector2i> line_breaks = TS->shaped_text_get_line_breaks(rid, width, start, flags);
for (int i = 0; i < line_breaks.size(); i++) { for (int i = 0; i < line_breaks.size(); i++) {
RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x); RID line = TS->shaped_text_substr(rid, line_breaks[i].x, line_breaks[i].y - line_breaks[i].x);
if (!tab_stops.is_empty()) { if (!tab_stops.is_empty()) {
@ -139,6 +185,10 @@ RID TextParagraph::get_line_rid(int p_line) const {
return lines[p_line]; return lines[p_line];
} }
RID TextParagraph::get_dropcap_rid() const {
return dropcap_rid;
}
void TextParagraph::clear() { void TextParagraph::clear() {
spacing_top = 0; spacing_top = 0;
spacing_bottom = 0; spacing_bottom = 0;
@ -147,10 +197,12 @@ void TextParagraph::clear() {
} }
lines.clear(); lines.clear();
TS->shaped_text_clear(rid); TS->shaped_text_clear(rid);
TS->shaped_text_clear(dropcap_rid);
} }
void TextParagraph::set_preserve_invalid(bool p_enabled) { void TextParagraph::set_preserve_invalid(bool p_enabled) {
TS->shaped_text_set_preserve_invalid(rid, p_enabled); TS->shaped_text_set_preserve_invalid(rid, p_enabled);
TS->shaped_text_set_preserve_invalid(dropcap_rid, p_enabled);
dirty_lines = true; dirty_lines = true;
} }
@ -160,6 +212,7 @@ bool TextParagraph::get_preserve_invalid() const {
void TextParagraph::set_preserve_control(bool p_enabled) { void TextParagraph::set_preserve_control(bool p_enabled) {
TS->shaped_text_set_preserve_control(rid, p_enabled); TS->shaped_text_set_preserve_control(rid, p_enabled);
TS->shaped_text_set_preserve_control(dropcap_rid, p_enabled);
dirty_lines = true; dirty_lines = true;
} }
@ -169,6 +222,7 @@ bool TextParagraph::get_preserve_control() const {
void TextParagraph::set_direction(TextServer::Direction p_direction) { void TextParagraph::set_direction(TextServer::Direction p_direction) {
TS->shaped_text_set_direction(rid, p_direction); TS->shaped_text_set_direction(rid, p_direction);
TS->shaped_text_set_direction(dropcap_rid, p_direction);
dirty_lines = true; dirty_lines = true;
} }
@ -179,6 +233,7 @@ TextServer::Direction TextParagraph::get_direction() const {
void TextParagraph::set_orientation(TextServer::Orientation p_orientation) { void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
TS->shaped_text_set_orientation(rid, p_orientation); TS->shaped_text_set_orientation(rid, p_orientation);
TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
dirty_lines = true; dirty_lines = true;
} }
@ -187,6 +242,20 @@ TextServer::Orientation TextParagraph::get_orientation() const {
return TS->shaped_text_get_orientation(rid); return TS->shaped_text_get_orientation(rid);
} }
bool TextParagraph::set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins, const Dictionary &p_opentype_features, const String &p_language) {
TS->shaped_text_clear(dropcap_rid);
dropcap_margins = p_dropcap_margins;
bool res = TS->shaped_text_add_string(dropcap_rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
dirty_lines = true;
return res;
}
void TextParagraph::clear_dropcap() {
dropcap_margins = Rect2();
TS->shaped_text_clear(dropcap_rid);
dirty_lines = true;
}
bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) { bool TextParagraph::add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language); bool res = TS->shaped_text_add_string(rid, p_text, p_fonts->get_rids(), p_size, p_opentype_features, p_language);
spacing_top = p_fonts->get_spacing(Font::SPACING_TOP); spacing_top = p_fonts->get_spacing(Font::SPACING_TOP);
@ -359,16 +428,57 @@ float TextParagraph::get_line_underline_thickness(int p_line) const {
return TS->shaped_text_get_underline_thickness(lines[p_line]); return TS->shaped_text_get_underline_thickness(lines[p_line]);
} }
void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const { Size2 TextParagraph::get_dropcap_size() const {
return TS->shaped_text_get_size(dropcap_rid) + dropcap_margins.size + dropcap_margins.position;
}
int TextParagraph::get_dropcap_lines() const {
return dropcap_lines;
}
void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color, const Color &p_dc_color) const {
const_cast<TextParagraph *>(this)->_shape_lines(); const_cast<TextParagraph *>(this)->_shape_lines();
Vector2 ofs = p_pos; Vector2 ofs = p_pos;
float h_offset = 0.f;
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
} else {
h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
}
if (h_offset > 0) {
// Draw dropcap.
Vector2 dc_off = ofs;
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
dc_off.x += width - h_offset;
} else {
dc_off.y += width - h_offset;
}
}
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);
}
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
float l_width = width;
if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x; ofs.x = p_pos.x;
ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top; ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
if (i <= dropcap_lines) {
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
}
l_width -= h_offset;
}
} else { } else {
ofs.y = p_pos.y; ofs.y = p_pos.y;
ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top; ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
if (i <= dropcap_lines) {
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
}
l_width -= h_offset;
}
} }
float length = TS->shaped_text_get_width(lines[i]); float length = TS->shaped_text_get_width(lines[i]);
if (width > 0) { if (width > 0) {
@ -378,16 +488,16 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
break; break;
case HALIGN_CENTER: { case HALIGN_CENTER: {
if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += Math::floor((width - length) / 2.0); ofs.x += Math::floor((l_width - length) / 2.0);
} else { } else {
ofs.y += Math::floor((width - length) / 2.0); ofs.y += Math::floor((l_width - length) / 2.0);
} }
} break; } break;
case HALIGN_RIGHT: { case HALIGN_RIGHT: {
if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += width - length; ofs.x += l_width - length;
} else { } else {
ofs.y += width - length; ofs.y += l_width - length;
} }
} break; } break;
} }
@ -398,7 +508,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
} else { } else {
clip_l = MAX(0, p_pos.y - ofs.y); clip_l = MAX(0, p_pos.y - ofs.y);
} }
TS->shaped_text_draw(lines[i], p_canvas, ofs, clip_l, clip_l + width, p_color); TS->shaped_text_draw(lines[i], p_canvas, ofs, clip_l, clip_l + l_width, p_color);
if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x; ofs.x = p_pos.x;
ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom; ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
@ -409,16 +519,50 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
} }
} }
void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const { void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color, const Color &p_dc_color) const {
const_cast<TextParagraph *>(this)->_shape_lines(); const_cast<TextParagraph *>(this)->_shape_lines();
Vector2 ofs = p_pos; Vector2 ofs = p_pos;
float h_offset = 0.f;
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
} else {
h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
}
if (h_offset > 0) {
// Draw dropcap.
Vector2 dc_off = ofs;
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
dc_off.x += width - h_offset;
} else {
dc_off.y += width - h_offset;
}
}
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);
}
for (int i = 0; i < lines.size(); i++) { for (int i = 0; i < lines.size(); i++) {
float l_width = width;
if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x; ofs.x = p_pos.x;
ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top; ofs.y += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
if (i <= dropcap_lines) {
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
}
l_width -= h_offset;
}
} else { } else {
ofs.y = p_pos.y; ofs.y = p_pos.y;
ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top; ofs.x += TS->shaped_text_get_ascent(lines[i]) + spacing_top;
if (i <= dropcap_lines) {
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
ofs.x -= h_offset;
}
l_width -= h_offset;
}
} }
float length = TS->shaped_text_get_width(lines[i]); float length = TS->shaped_text_get_width(lines[i]);
if (width > 0) { if (width > 0) {
@ -428,16 +572,16 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
break; break;
case HALIGN_CENTER: { case HALIGN_CENTER: {
if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += Math::floor((width - length) / 2.0); ofs.x += Math::floor((l_width - length) / 2.0);
} else { } else {
ofs.y += Math::floor((width - length) / 2.0); ofs.y += Math::floor((l_width - length) / 2.0);
} }
} break; } break;
case HALIGN_RIGHT: { case HALIGN_RIGHT: {
if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += width - length; ofs.x += l_width - length;
} else { } else {
ofs.y += width - length; ofs.y += l_width - length;
} }
} break; } break;
} }
@ -448,7 +592,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
} else { } else {
clip_l = MAX(0, p_pos.y - ofs.y); clip_l = MAX(0, p_pos.y - ofs.y);
} }
TS->shaped_text_draw_outline(lines[i], p_canvas, ofs, clip_l, clip_l + width, p_outline_size, p_color); TS->shaped_text_draw_outline(lines[i], p_canvas, ofs, clip_l, clip_l + l_width, p_outline_size, p_color);
if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) { if (TS->shaped_text_get_orientation(lines[i]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x = p_pos.x; ofs.x = p_pos.x;
ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom; ofs.y += TS->shaped_text_get_descent(lines[i]) + spacing_bottom;
@ -485,11 +629,56 @@ int TextParagraph::hit_test(const Point2 &p_coords) const {
return TS->shaped_text_get_range(rid).y; return TS->shaped_text_get_range(rid).y;
} }
void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color) const {
Vector2 ofs = p_pos;
float h_offset = 0.f;
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
} else {
h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
}
if (h_offset > 0) {
// Draw dropcap.
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += width - h_offset;
} else {
ofs.y += width - h_offset;
}
}
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);
}
}
void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size, const Color &p_color) const {
Vector2 ofs = p_pos;
float h_offset = 0.f;
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
h_offset = TS->shaped_text_get_size(dropcap_rid).x + dropcap_margins.size.x + dropcap_margins.position.x;
} else {
h_offset = TS->shaped_text_get_size(dropcap_rid).y + dropcap_margins.size.y + dropcap_margins.position.y;
}
if (h_offset > 0) {
// Draw dropcap.
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.x += width - h_offset;
} else {
ofs.y += width - h_offset;
}
}
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);
}
}
void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color) const { void TextParagraph::draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color) const {
const_cast<TextParagraph *>(this)->_shape_lines(); const_cast<TextParagraph *>(this)->_shape_lines();
ERR_FAIL_COND(p_line < 0 || p_line >= lines.size()); ERR_FAIL_COND(p_line < 0 || p_line >= lines.size());
Vector2 ofs = p_pos; Vector2 ofs = p_pos;
if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) { if (TS->shaped_text_get_orientation(lines[p_line]) == TextServer::ORIENTATION_HORIZONTAL) {
ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top; ofs.y += TS->shaped_text_get_ascent(lines[p_line]) + spacing_top;
} else { } else {
@ -521,6 +710,7 @@ TextParagraph::TextParagraph(const String &p_text, const Ref<Font> &p_fonts, int
TextParagraph::TextParagraph() { TextParagraph::TextParagraph() {
rid = TS->create_shaped_text(); rid = TS->create_shaped_text();
dropcap_rid = TS->create_shaped_text();
} }
TextParagraph::~TextParagraph() { TextParagraph::~TextParagraph() {
@ -529,4 +719,5 @@ TextParagraph::~TextParagraph() {
} }
lines.clear(); lines.clear();
TS->free(rid); TS->free(rid);
TS->free(dropcap_rid);
} }

View file

@ -39,6 +39,10 @@
class TextParagraph : public Reference { class TextParagraph : public Reference {
GDCLASS(TextParagraph, Reference); GDCLASS(TextParagraph, Reference);
RID dropcap_rid;
int dropcap_lines = 0;
Rect2 dropcap_margins;
RID rid; RID rid;
Vector<RID> lines; Vector<RID> lines;
int spacing_top = 0; int spacing_top = 0;
@ -60,6 +64,7 @@ protected:
public: public:
RID get_rid() const; RID get_rid() const;
RID get_line_rid(int p_line) const; RID get_line_rid(int p_line) const;
RID get_dropcap_rid() const;
void clear(); void clear();
@ -77,6 +82,9 @@ public:
void set_bidi_override(const Vector<Vector2i> &p_override); void set_bidi_override(const Vector<Vector2i> &p_override);
bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
void clear_dropcap();
bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = ""); bool add_string(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1); bool add_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER, int p_length = 1);
bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER); bool resize_object(Variant p_key, const Size2 &p_size, VAlign p_inline_align = VALIGN_CENTER);
@ -108,12 +116,18 @@ public:
float get_line_underline_position(int p_line) const; float get_line_underline_position(int p_line) const;
float get_line_underline_thickness(int p_line) const; float get_line_underline_thickness(int p_line) const;
void draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1)) const; Size2 get_dropcap_size() const;
void draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const; int get_dropcap_lines() const;
void draw(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1), const Color &p_dc_color = Color(1, 1, 1)) const;
void draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1), const Color &p_dc_color = Color(1, 1, 1)) const;
void draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color = Color(1, 1, 1)) const; void draw_line(RID p_canvas, const Vector2 &p_pos, int p_line, const Color &p_color = Color(1, 1, 1)) const;
void draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const; void draw_line_outline(RID p_canvas, const Vector2 &p_pos, int p_line, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const;
void draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color &p_color = Color(1, 1, 1)) const;
void draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int p_outline_size = 1, const Color &p_color = Color(1, 1, 1)) const;
int hit_test(const Point2 &p_coords) const; int hit_test(const Point2 &p_coords) const;
void _set_bidi_override(const Array &p_override); void _set_bidi_override(const Array &p_override);