[Label] Add get_character_bounds method to get bounding rectangles of the characters.
This commit is contained in:
parent
bf8dd73e9d
commit
e9c219cff2
|
@ -10,6 +10,13 @@
|
|||
<link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link>
|
||||
</tutorials>
|
||||
<methods>
|
||||
<method name="get_character_bounds" qualifiers="const">
|
||||
<return type="Rect2" />
|
||||
<param index="0" name="pos" type="int" />
|
||||
<description>
|
||||
Returns the bounding rectangle of the character at position [param pos]. If the character is a non-visual character or [param pos] is outside the valid range, an empty [Rect2] is returned. If the character is a part of a composite grapheme, the bounding rectangle of the whole grapheme is returned.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_line_count" qualifiers="const">
|
||||
<return type="int" />
|
||||
<description>
|
||||
|
|
|
@ -668,6 +668,127 @@ void Label::_notification(int p_what) {
|
|||
}
|
||||
}
|
||||
|
||||
Rect2 Label::get_character_bounds(int p_pos) const {
|
||||
if (dirty || font_dirty || lines_dirty) {
|
||||
const_cast<Label *>(this)->_shape();
|
||||
}
|
||||
|
||||
bool has_settings = settings.is_valid();
|
||||
Size2 size = get_size();
|
||||
Ref<StyleBox> style = theme_cache.normal_style;
|
||||
int line_spacing = has_settings ? settings->get_line_spacing() : theme_cache.line_spacing;
|
||||
bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL);
|
||||
bool rtl_layout = is_layout_rtl();
|
||||
|
||||
float total_h = 0.0;
|
||||
int lines_visible = 0;
|
||||
|
||||
// Get number of lines to fit to the height.
|
||||
for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
|
||||
total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
|
||||
if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
|
||||
break;
|
||||
}
|
||||
lines_visible++;
|
||||
}
|
||||
|
||||
if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
|
||||
lines_visible = max_lines_visible;
|
||||
}
|
||||
|
||||
int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
|
||||
|
||||
// Get real total height.
|
||||
total_h = 0;
|
||||
for (int64_t i = lines_skipped; i < last_line; i++) {
|
||||
total_h += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
|
||||
}
|
||||
|
||||
total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM);
|
||||
|
||||
int vbegin = 0, vsep = 0;
|
||||
if (lines_visible > 0) {
|
||||
switch (vertical_alignment) {
|
||||
case VERTICAL_ALIGNMENT_TOP: {
|
||||
// Nothing.
|
||||
} break;
|
||||
case VERTICAL_ALIGNMENT_CENTER: {
|
||||
vbegin = (size.y - (total_h - line_spacing)) / 2;
|
||||
vsep = 0;
|
||||
|
||||
} break;
|
||||
case VERTICAL_ALIGNMENT_BOTTOM: {
|
||||
vbegin = size.y - (total_h - line_spacing);
|
||||
vsep = 0;
|
||||
|
||||
} break;
|
||||
case VERTICAL_ALIGNMENT_FILL: {
|
||||
vbegin = 0;
|
||||
if (lines_visible > 1) {
|
||||
vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1);
|
||||
} else {
|
||||
vsep = 0;
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 ofs;
|
||||
ofs.y = style->get_offset().y + vbegin;
|
||||
for (int i = lines_skipped; i < last_line; i++) {
|
||||
Size2 line_size = TS->shaped_text_get_size(lines_rid[i]);
|
||||
ofs.x = 0;
|
||||
switch (horizontal_alignment) {
|
||||
case HORIZONTAL_ALIGNMENT_FILL:
|
||||
if (rtl && autowrap_mode != TextServer::AUTOWRAP_OFF) {
|
||||
ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
|
||||
} else {
|
||||
ofs.x = style->get_offset().x;
|
||||
}
|
||||
break;
|
||||
case HORIZONTAL_ALIGNMENT_LEFT: {
|
||||
if (rtl_layout) {
|
||||
ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
|
||||
} else {
|
||||
ofs.x = style->get_offset().x;
|
||||
}
|
||||
} break;
|
||||
case HORIZONTAL_ALIGNMENT_CENTER: {
|
||||
ofs.x = int(size.width - line_size.width) / 2;
|
||||
} break;
|
||||
case HORIZONTAL_ALIGNMENT_RIGHT: {
|
||||
if (rtl_layout) {
|
||||
ofs.x = style->get_offset().x;
|
||||
} else {
|
||||
ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
int v_size = TS->shaped_text_get_glyph_count(lines_rid[i]);
|
||||
const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
|
||||
|
||||
float gl_off = 0.0f;
|
||||
for (int j = 0; j < v_size; j++) {
|
||||
if ((glyphs[j].count > 0) && ((glyphs[j].index != 0) || ((glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE))) {
|
||||
if (p_pos >= glyphs[j].start && p_pos < glyphs[j].end) {
|
||||
float advance = 0.f;
|
||||
for (int k = 0; k < glyphs[j].count; k++) {
|
||||
advance += glyphs[j + k].advance;
|
||||
}
|
||||
Rect2 rect;
|
||||
rect.position = ofs + Vector2(gl_off, 0);
|
||||
rect.size = Vector2(advance, TS->shaped_text_get_size(lines_rid[i]).y);
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
gl_off += glyphs[j].advance * glyphs[j].repeat;
|
||||
}
|
||||
ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing;
|
||||
}
|
||||
return Rect2();
|
||||
}
|
||||
|
||||
Size2 Label::get_minimum_size() const {
|
||||
// don't want to mutable everything
|
||||
if (dirty || font_dirty || lines_dirty) {
|
||||
|
@ -1055,6 +1176,8 @@ void Label::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label::set_structured_text_bidi_override_options);
|
||||
ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_character_bounds", "pos"), &Label::get_character_bounds);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "label_settings", PROPERTY_HINT_RESOURCE_TYPE, "LabelSettings"), "set_label_settings", "get_label_settings");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
|
||||
|
|
|
@ -161,6 +161,8 @@ public:
|
|||
int get_line_count() const;
|
||||
int get_visible_line_count() const;
|
||||
|
||||
Rect2 get_character_bounds(int p_pos) const;
|
||||
|
||||
Label(const String &p_text = String());
|
||||
~Label();
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue