From ac931c33c2cec0ce3738e725efe8bce7f540ed44 Mon Sep 17 00:00:00 2001 From: DE YU <71481700+Delsin-Yu@users.noreply.github.com> Date: Wed, 2 Apr 2025 21:16:53 +0800 Subject: [PATCH] Implmement stackable text effects on label type through label settings Co-authored-by: Micky <66727710+Mickeon@users.noreply.github.com> Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> --- doc/classes/LabelSettings.xml | 127 +++++++++++++++++++ scene/gui/label.cpp | 146 +++++++++------------ scene/gui/label.h | 59 +++++++-- scene/resources/label_settings.cpp | 196 +++++++++++++++++++++++++++++ scene/resources/label_settings.h | 66 ++++++++++ 5 files changed, 500 insertions(+), 94 deletions(-) diff --git a/doc/classes/LabelSettings.xml b/doc/classes/LabelSettings.xml index 610f10574b5..03d75066d6a 100644 --- a/doc/classes/LabelSettings.xml +++ b/doc/classes/LabelSettings.xml @@ -8,6 +8,127 @@ + + + + + + Adds a new stacked outline to the label at the given [param index]. If [param index] is [code]-1[/code], the new stacked outline will be added at the end of the list. + + + + + + + Adds a new stacked shadow to the label at the given [param index]. If [param index] is [code]-1[/code], the new stacked shadow will be added at the end of the list. + + + + + + + Returns the color of the stacked outline at [param index]. + + + + + + + Returns the size of the stacked outline at [param index]. + + + + + + + Returns the color of the stacked shadow at [param index]. + + + + + + + Returns the offset of the stacked shadow at [param index]. + + + + + + + Returns the outline size of the stacked shadow at [param index]. + + + + + + + + Moves the stacked outline at index [param from_index] to the given position [param to_position] in the array. + + + + + + + + Moves the stacked shadow at index [param from_index] to the given position [param to_position] in the array. + + + + + + + Removes the stacked outline at index [param index]. + + + + + + + Removes the stacked shadow at index [param index]. + + + + + + + + Sets the color of the stacked outline identified by the given [param index] to [param color]. + + + + + + + + Sets the size of the stacked outline identified by the given [param index] to [param size]. + + + + + + + + Sets the color of the stacked shadow identified by the given [param index] to [param color]. + + + + + + + + Sets the offset of the stacked shadow identified by the given [param index] to [param offset]. + + + + + + + + Sets the outline size of the stacked shadow identified by the given [param index] to [param size]. + + + [Font] used for the text. @@ -39,5 +160,11 @@ Size of the shadow effect. + + The number of stacked outlines. + + + Returns the stacked shadow count. + diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index f763cad51cc..eddaa5d819c 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -430,13 +430,13 @@ inline void draw_glyph_shadow(const Glyph &p_gl, const RID &p_canvas, const Colo } } -inline void draw_glyph_shadow_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_shadow_color, int p_shadow_outline_size, const Vector2 &p_ofs, const Vector2 &shadow_ofs) { +inline void draw_glyph_shadow_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_shadow_color, const Vector2 &p_ofs, int p_shadow_outline_size, const Vector2 &shadow_ofs) { if (p_gl.font_rid != RID()) { TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color); } } -inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_outline_color, int p_outline_size, const Vector2 &p_ofs) { +inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_outline_color, const Vector2 &p_ofs, int p_outline_size) { if (p_gl.font_rid != RID()) { if (p_font_outline_color.a != 0.0 && p_outline_size > 0) { TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_outline_color); @@ -747,6 +747,8 @@ void Label::_notification(int p_what) { Color font_outline_color = has_settings ? settings->get_outline_color() : theme_cache.font_outline_color; int outline_size = has_settings ? settings->get_outline_size() : theme_cache.font_outline_size; int shadow_outline_size = has_settings ? settings->get_shadow_size() : theme_cache.font_shadow_outline_size; + Vector stacked_outline_datas = has_settings ? settings->get_stacked_outline_data() : Vector(); + Vector stacked_shadow_datas = has_settings ? settings->get_stacked_shadow_data() : Vector(); bool rtl_layout = is_layout_rtl(); style->draw(ci, Rect2(Point2(0, 0), get_size())); @@ -798,95 +800,67 @@ void Label::_notification(int p_what) { ofs.y += asc; - // Draw shadow, outline and text. Note: Do not merge this into the single loop iteration, to prevent overlaps. + // Draw text effects and main texts. Note: Do not merge this into the single loop iteration, to prevent overlaps. int processed_glyphs_step = 0; - for (int step = DRAW_STEP_SHADOW_OUTLINE; step < DRAW_STEP_MAX; step++) { - if (step == DRAW_STEP_SHADOW_OUTLINE && (font_shadow_color.a == 0 || shadow_outline_size <= 0)) { - continue; - } - if (step == DRAW_STEP_SHADOW && (font_shadow_color.a == 0)) { - continue; - } - if (step == DRAW_STEP_OUTLINE && (outline_size <= 0 || font_outline_color.a == 0)) { - continue; - } - processed_glyphs_step = processed_glyphs; - Vector2 offset_step = ofs; - // Draw RTL ellipsis string when necessary. - if (rtl && ellipsis_pos >= 0) { - for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { - for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { - bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end + para.start > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs)); - if (!skip) { - if (step == DRAW_STEP_SHADOW_OUTLINE) { - draw_glyph_shadow_outline(ellipsis_glyphs[gl_idx], ci, font_shadow_color, shadow_outline_size, offset_step, shadow_ofs); - } else if (step == DRAW_STEP_SHADOW) { - draw_glyph_shadow(ellipsis_glyphs[gl_idx], ci, font_shadow_color, offset_step, shadow_ofs); - } else if (step == DRAW_STEP_OUTLINE) { - draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_outline_color, outline_size, offset_step); - } else if (step == DRAW_STEP_TEXT) { - draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, offset_step); - } - } - processed_glyphs_step++; - offset_step.x += ellipsis_glyphs[gl_idx].advance; - } - } - } - // Draw main text. - for (int j = 0; j < gl_size; j++) { - // Trim when necessary. - if (trim_pos >= 0) { - if (rtl) { - if (j < trim_pos) { - continue; - } - } else { - if (j >= trim_pos) { - break; - } - } - } - for (int k = 0; k < glyphs[j].repeat; k++) { - bool skip = (trim_chars && glyphs[j].end + para.start > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs)); - if (!skip) { - if (step == DRAW_STEP_SHADOW_OUTLINE) { - draw_glyph_shadow_outline(glyphs[j], ci, font_shadow_color, shadow_outline_size, offset_step, shadow_ofs); - } else if (step == DRAW_STEP_SHADOW) { - draw_glyph_shadow(glyphs[j], ci, font_shadow_color, offset_step, shadow_ofs); - } else if (step == DRAW_STEP_OUTLINE) { - draw_glyph_outline(glyphs[j], ci, font_outline_color, outline_size, offset_step); - } else if (step == DRAW_STEP_TEXT) { - draw_glyph(glyphs[j], ci, font_color, offset_step); - } - } - processed_glyphs_step++; - offset_step.x += glyphs[j].advance; - } - } - // Draw LTR ellipsis string when necessary. - if (!rtl && ellipsis_pos >= 0) { - for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { - for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { - bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end + para.start > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs)); - if (!skip) { - if (step == DRAW_STEP_SHADOW_OUTLINE) { - draw_glyph_shadow_outline(ellipsis_glyphs[gl_idx], ci, font_shadow_color, shadow_outline_size, offset_step, shadow_ofs); - } else if (step == DRAW_STEP_SHADOW) { - draw_glyph_shadow(ellipsis_glyphs[gl_idx], ci, font_shadow_color, offset_step, shadow_ofs); - } else if (step == DRAW_STEP_OUTLINE) { - draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_outline_color, outline_size, offset_step); - } else if (step == DRAW_STEP_TEXT) { - draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, offset_step); - } - } - processed_glyphs_step++; - offset_step.x += ellipsis_glyphs[gl_idx].advance; - } + // Draw shadow outline. + if (font_shadow_color.a != 0 && shadow_outline_size > 0) { + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_shadow_color, draw_glyph_shadow_outline, shadow_outline_size, shadow_ofs); + } + + // Draw shadow. + if (font_shadow_color.a > 0) { + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_shadow_color, draw_glyph_shadow, shadow_ofs); + } + + // Draw stacked shadow. + if (stacked_shadow_datas.size() != 0) { + int draw_iterations = stacked_shadow_datas.size(); + + for (int draw_iteration_index = draw_iterations - 1; draw_iteration_index >= 0; --draw_iteration_index) { + LabelSettings::StackedShadowData stacked_shadow_data = stacked_shadow_datas[draw_iteration_index]; + if (stacked_shadow_data.outline_size > 0) { + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, stacked_shadow_data.color, draw_glyph_shadow_outline, stacked_shadow_data.outline_size, stacked_shadow_data.offset); } + + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, stacked_shadow_data.color, draw_glyph_shadow, stacked_shadow_data.offset); } } + + // Draw stacked outline. + if (stacked_outline_datas.size() != 0) { + int stacked_outline_draw_size = outline_size; + + int draw_iterations = stacked_outline_datas.size(); + + for (int j = 0; j < draw_iterations; j++) { + int stacked_outline_size = stacked_outline_datas[j].size; + if (stacked_outline_size <= 0) { + continue; + } + stacked_outline_draw_size += stacked_outline_size; + } + + for (int draw_iteration_index = draw_iterations - 1; draw_iteration_index >= 0; --draw_iteration_index) { + LabelSettings::StackedOutlineData stacked_outline_data = stacked_outline_datas[draw_iteration_index]; + if (stacked_outline_data.size <= 0) { + continue; + } + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, stacked_outline_data.color, draw_glyph_outline, stacked_outline_draw_size); + stacked_outline_draw_size -= stacked_outline_data.size; + } + } + + // Draw outline. + if (outline_size > 0 && font_outline_color.a != 0) { + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_outline_color, draw_glyph_outline, outline_size); + } + + // Draw text. + { + draw_text(rtl, ellipsis_pos, ellipsis_gl_size, ellipsis_glyphs, trim_chars, para.start, visible_chars, trim_glyphs_ltr, processed_glyphs_step, processed_glyphs, visible_glyphs, trim_glyphs_rtl, total_glyphs, ci, ofs, gl_size, trim_pos, glyphs, font_color, draw_glyph); + } + processed_glyphs = processed_glyphs_step; ofs.y += dsc + line_spacing; } diff --git a/scene/gui/label.h b/scene/gui/label.h index 34b25d77262..42a17843bcf 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -37,14 +37,6 @@ class Label : public Control { GDCLASS(Label, Control); private: - enum LabelDrawStep { - DRAW_STEP_SHADOW_OUTLINE, - DRAW_STEP_SHADOW, - DRAW_STEP_OUTLINE, - DRAW_STEP_TEXT, - DRAW_STEP_MAX, - }; - HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT; VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_TOP; String text; @@ -199,4 +191,55 @@ public: Label(const String &p_text = String()); ~Label(); + + template + void draw_text(bool p_rtl, int p_ellipsis_pos, int p_ellipsis_gl_size, const Glyph *p_ellipsis_glyphs, bool p_trim_chars, int p_para_start, int p_visible_chars, bool p_trim_glyphs_ltr, int &p_processed_glyphs_step, int p_processed_glyphs, int p_visible_glyphs, bool p_trim_glyphs_rtl, int p_total_glyphs, const RID &p_ci, const Vector2 &p_ofs, int p_gl_size, int p_trim_pos, const Glyph *p_glyphs, const Color &p_color, void (*p_draw_func)(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_outline_color, const Vector2 &p_ofs, VarArgsFunc... p_args), VarArgs &&...p_args) { + p_processed_glyphs_step = p_processed_glyphs; + Vector2 offset_step = p_ofs; /* Draw RTL ellipsis string when necessary. */ + if (p_rtl && p_ellipsis_pos >= 0) { + for (int gl_idx = p_ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { + for (int j = 0; j < p_ellipsis_glyphs[gl_idx].repeat; j++) { + bool skip = (p_trim_chars && p_ellipsis_glyphs[gl_idx].end + p_para_start > p_visible_chars) || (p_trim_glyphs_ltr && (p_processed_glyphs_step >= p_visible_glyphs)) || (p_trim_glyphs_rtl && (p_processed_glyphs_step < p_total_glyphs - p_visible_glyphs)); + if (!skip) { + p_draw_func(p_ellipsis_glyphs[gl_idx], p_ci, p_color, offset_step, std::forward(p_args)...); + } + p_processed_glyphs_step++; + offset_step.x += p_ellipsis_glyphs[gl_idx].advance; + } + } + } /* Draw main text. */ + for (int j = 0; j < p_gl_size; j++) { /* Trim when necessary. */ + if (p_trim_pos >= 0) { + if (p_rtl) { + if (j < p_trim_pos) { + continue; + } + } else { + if (j >= p_trim_pos) { + break; + } + } + } + for (int k = 0; k < p_glyphs[j].repeat; k++) { + bool skip = (p_trim_chars && p_glyphs[j].end + p_para_start > p_visible_chars) || (p_trim_glyphs_ltr && (p_processed_glyphs_step >= p_visible_glyphs)) || (p_trim_glyphs_rtl && (p_processed_glyphs_step < p_total_glyphs - p_visible_glyphs)); + if (!skip) { + p_draw_func(p_glyphs[j], p_ci, p_color, offset_step, std::forward(p_args)...); + } + p_processed_glyphs_step++; + offset_step.x += p_glyphs[j].advance; + } + } /* Draw LTR ellipsis string when necessary. */ + if (!p_rtl && p_ellipsis_pos >= 0) { + for (int gl_idx = 0; gl_idx < p_ellipsis_gl_size; gl_idx++) { + for (int j = 0; j < p_ellipsis_glyphs[gl_idx].repeat; j++) { + bool skip = (p_trim_chars && p_ellipsis_glyphs[gl_idx].end + p_para_start > p_visible_chars) || (p_trim_glyphs_ltr && (p_processed_glyphs_step >= p_visible_glyphs)) || (p_trim_glyphs_rtl && (p_processed_glyphs_step < p_total_glyphs - p_visible_glyphs)); + if (!skip) { + p_draw_func(p_ellipsis_glyphs[gl_idx], p_ci, p_color, offset_step, std::forward(p_args)...); + } + p_processed_glyphs_step++; + offset_step.x += p_ellipsis_glyphs[gl_idx].advance; + } + } + } + } }; diff --git a/scene/resources/label_settings.cpp b/scene/resources/label_settings.cpp index 2ceb033a2dd..82f39e7299d 100644 --- a/scene/resources/label_settings.cpp +++ b/scene/resources/label_settings.cpp @@ -65,6 +65,30 @@ void LabelSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_offset", "offset"), &LabelSettings::set_shadow_offset); ClassDB::bind_method(D_METHOD("get_shadow_offset"), &LabelSettings::get_shadow_offset); + // Stacked outlines + ClassDB::bind_method(D_METHOD("get_stacked_outline_count"), &LabelSettings::get_stacked_outline_count); + ClassDB::bind_method(D_METHOD("set_stacked_outline_count", "count"), &LabelSettings::set_stacked_outline_count); + ClassDB::bind_method(D_METHOD("add_stacked_outline", "index"), &LabelSettings::add_stacked_outline, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_stacked_outline", "from_index", "to_position"), &LabelSettings::move_stacked_outline); + ClassDB::bind_method(D_METHOD("remove_stacked_outline", "index"), &LabelSettings::remove_stacked_outline); + ClassDB::bind_method(D_METHOD("set_stacked_outline_size", "index", "size"), &LabelSettings::set_stacked_outline_size); + ClassDB::bind_method(D_METHOD("get_stacked_outline_size", "index"), &LabelSettings::get_stacked_outline_size); + ClassDB::bind_method(D_METHOD("set_stacked_outline_color", "index", "color"), &LabelSettings::set_stacked_outline_color); + ClassDB::bind_method(D_METHOD("get_stacked_outline_color", "index"), &LabelSettings::get_stacked_outline_color); + + // Stacked shadows + ClassDB::bind_method(D_METHOD("get_stacked_shadow_count"), &LabelSettings::get_stacked_shadow_count); + ClassDB::bind_method(D_METHOD("set_stacked_shadow_count", "count"), &LabelSettings::set_stacked_shadow_count); + ClassDB::bind_method(D_METHOD("add_stacked_shadow", "index"), &LabelSettings::add_stacked_shadow, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("move_stacked_shadow", "from_index", "to_position"), &LabelSettings::move_stacked_shadow); + ClassDB::bind_method(D_METHOD("remove_stacked_shadow", "index"), &LabelSettings::remove_stacked_shadow); + ClassDB::bind_method(D_METHOD("set_stacked_shadow_offset", "index", "offset"), &LabelSettings::set_stacked_shadow_offset); + ClassDB::bind_method(D_METHOD("get_stacked_shadow_offset", "index"), &LabelSettings::get_stacked_shadow_offset); + ClassDB::bind_method(D_METHOD("set_stacked_shadow_color", "index", "color"), &LabelSettings::set_stacked_shadow_color); + ClassDB::bind_method(D_METHOD("get_stacked_shadow_color", "index"), &LabelSettings::get_stacked_shadow_color); + ClassDB::bind_method(D_METHOD("set_stacked_shadow_outline_size", "index", "size"), &LabelSettings::set_stacked_shadow_outline_size); + ClassDB::bind_method(D_METHOD("get_stacked_shadow_outline_size", "index"), &LabelSettings::get_stacked_shadow_outline_size); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "line_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_line_spacing", "get_line_spacing"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "paragraph_spacing", PROPERTY_HINT_NONE, "suffix:px"), "set_paragraph_spacing", "get_paragraph_spacing"); @@ -81,6 +105,27 @@ void LabelSettings::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_size", PROPERTY_HINT_RANGE, "0,127,1,or_greater,suffix:px"), "set_shadow_size", "get_shadow_size"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "shadow_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_shadow_offset", "get_shadow_offset"); + + ADD_GROUP("Stacked Effects", ""); + ADD_ARRAY_COUNT("Stacked Outlines", "stacked_outline_count", "set_stacked_outline_count", "get_stacked_outline_count", "stacked_outline_"); + ADD_ARRAY_COUNT("Stacked Shadows", "stacked_shadow_count", "set_stacked_shadow_count", "get_stacked_shadow_count", "stacked_shadow_"); + + constexpr StackedOutlineData stacked_outline_defaults; + + stacked_outline_base_property_helper.set_prefix("stacked_outline_"); + stacked_outline_base_property_helper.set_array_length_getter(&LabelSettings::get_stacked_outline_count); + stacked_outline_base_property_helper.register_property(PropertyInfo(Variant::INT, "size", PROPERTY_HINT_NONE, "0,127,1,or_greater,suffix:px"), stacked_outline_defaults.size, &LabelSettings::set_stacked_outline_size, &LabelSettings::get_stacked_outline_size); + stacked_outline_base_property_helper.register_property(PropertyInfo(Variant::COLOR, "color"), stacked_outline_defaults.color, &LabelSettings::set_stacked_outline_color, &LabelSettings::get_stacked_outline_color); + PropertyListHelper::register_base_helper(&stacked_outline_base_property_helper); + + constexpr StackedShadowData stacked_shadow_defaults; + + stacked_shadow_base_property_helper.set_prefix("stacked_shadow_"); + stacked_shadow_base_property_helper.set_array_length_getter(&LabelSettings::get_stacked_shadow_count); + stacked_shadow_base_property_helper.register_property(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), stacked_shadow_defaults.offset, &LabelSettings::set_stacked_shadow_offset, &LabelSettings::get_stacked_shadow_offset); + stacked_shadow_base_property_helper.register_property(PropertyInfo(Variant::COLOR, "color"), stacked_shadow_defaults.color, &LabelSettings::set_stacked_shadow_color, &LabelSettings::get_stacked_shadow_color); + stacked_shadow_base_property_helper.register_property(PropertyInfo(Variant::INT, "outline_size", PROPERTY_HINT_NONE, "0,127,1,or_greater,suffix:px"), stacked_shadow_defaults.outline_size, &LabelSettings::set_stacked_shadow_outline_size, &LabelSettings::get_stacked_shadow_outline_size); + PropertyListHelper::register_base_helper(&stacked_shadow_base_property_helper); } void LabelSettings::set_line_spacing(real_t p_spacing) { @@ -198,3 +243,154 @@ void LabelSettings::set_shadow_offset(const Vector2 &p_offset) { Vector2 LabelSettings::get_shadow_offset() const { return shadow_offset; } + +Vector LabelSettings::get_stacked_outline_data() const { + return stacked_outline_data; +} + +int LabelSettings::get_stacked_outline_count() const { + return stacked_outline_data.size(); +} + +void LabelSettings::set_stacked_outline_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + if (stacked_outline_data.size() != p_count) { + stacked_outline_data.resize(p_count); + notify_property_list_changed(); + emit_changed(); + } +} + +void LabelSettings::add_stacked_outline(int p_index) { + if (p_index < 0) { + p_index = stacked_outline_data.size(); + } + ERR_FAIL_INDEX(p_index, stacked_outline_data.size() + 1); + stacked_outline_data.insert(p_index, StackedOutlineData()); + notify_property_list_changed(); + emit_changed(); +} + +void LabelSettings::move_stacked_outline(int p_from_index, int p_to_position) { + ERR_FAIL_INDEX(p_from_index, stacked_outline_data.size()); + ERR_FAIL_INDEX(p_to_position, stacked_outline_data.size() + 1); + stacked_outline_data.insert(p_to_position, stacked_outline_data[p_from_index]); + stacked_outline_data.remove_at(p_to_position < p_from_index ? p_from_index + 1 : p_from_index); + notify_property_list_changed(); + emit_changed(); +} + +void LabelSettings::remove_stacked_outline(int p_index) { + ERR_FAIL_INDEX(p_index, stacked_outline_data.size()); + stacked_outline_data.remove_at(p_index); + notify_property_list_changed(); + emit_changed(); +} + +void LabelSettings::set_stacked_outline_size(int p_index, int p_size) { + ERR_FAIL_INDEX(p_index, stacked_outline_data.size()); + if (stacked_outline_data[p_index].size != p_size) { + stacked_outline_data.write[p_index].size = p_size; + emit_changed(); + } +} + +int LabelSettings::get_stacked_outline_size(int p_index) const { + ERR_FAIL_INDEX_V(p_index, stacked_outline_data.size(), 0); + return stacked_outline_data[p_index].size; +} + +void LabelSettings::set_stacked_outline_color(int p_index, const Color &p_color) { + ERR_FAIL_INDEX(p_index, stacked_outline_data.size()); + if (stacked_outline_data[p_index].color != p_color) { + stacked_outline_data.write[p_index].color = p_color; + emit_changed(); + } +} + +Color LabelSettings::get_stacked_outline_color(int p_index) const { + ERR_FAIL_INDEX_V(p_index, stacked_outline_data.size(), Color()); + return stacked_outline_data[p_index].color; +} + +Vector LabelSettings::get_stacked_shadow_data() const { + return stacked_shadow_data; +} + +int LabelSettings::get_stacked_shadow_count() const { + return stacked_shadow_data.size(); +} + +void LabelSettings::set_stacked_shadow_count(int p_count) { + ERR_FAIL_COND(p_count < 0); + if (stacked_shadow_data.size() != p_count) { + stacked_shadow_data.resize(p_count); + notify_property_list_changed(); + emit_changed(); + } +} + +void LabelSettings::add_stacked_shadow(int p_index) { + if (p_index < 0) { + p_index = stacked_shadow_data.size(); + } + ERR_FAIL_INDEX(p_index, stacked_shadow_data.size() + 1); + stacked_shadow_data.insert(p_index, StackedShadowData()); + notify_property_list_changed(); + emit_changed(); +} + +void LabelSettings::move_stacked_shadow(int p_from_index, int p_to_position) { + ERR_FAIL_INDEX(p_from_index, stacked_shadow_data.size()); + ERR_FAIL_INDEX(p_to_position, stacked_shadow_data.size() + 1); + stacked_shadow_data.insert(p_to_position, stacked_shadow_data[p_from_index]); + stacked_shadow_data.remove_at(p_to_position < p_from_index ? p_from_index + 1 : p_from_index); + notify_property_list_changed(); + emit_changed(); +} + +void LabelSettings::remove_stacked_shadow(int p_index) { + ERR_FAIL_INDEX(p_index, stacked_shadow_data.size()); + stacked_shadow_data.remove_at(p_index); + notify_property_list_changed(); + emit_changed(); +} + +void LabelSettings::set_stacked_shadow_offset(int p_index, const Vector2 &p_offset) { + ERR_FAIL_INDEX(p_index, stacked_shadow_data.size()); + if (stacked_shadow_data[p_index].offset != p_offset) { + stacked_shadow_data.write[p_index].offset = p_offset; + emit_changed(); + } +} + +Vector2 LabelSettings::get_stacked_shadow_offset(int p_index) const { + ERR_FAIL_INDEX_V(p_index, stacked_shadow_data.size(), Vector2()); + return stacked_shadow_data[p_index].offset; +} + +void LabelSettings::set_stacked_shadow_color(int p_index, const Color &p_color) { + ERR_FAIL_INDEX(p_index, stacked_shadow_data.size()); + if (stacked_shadow_data[p_index].color != p_color) { + stacked_shadow_data.write[p_index].color = p_color; + emit_changed(); + } +} + +Color LabelSettings::get_stacked_shadow_color(int p_index) const { + ERR_FAIL_INDEX_V(p_index, stacked_shadow_data.size(), Color()); + return stacked_shadow_data[p_index].color; +} + +void LabelSettings::set_stacked_shadow_outline_size(int p_index, int p_size) { + ERR_FAIL_INDEX(p_index, stacked_shadow_data.size()); + if (stacked_shadow_data[p_index].outline_size != p_size) { + stacked_shadow_data.write[p_index].outline_size = p_size; + emit_changed(); + } +} + +int LabelSettings::get_stacked_shadow_outline_size(int p_index) const { + ERR_FAIL_INDEX_V(p_index, stacked_shadow_data.size(), 0); + return stacked_shadow_data[p_index].outline_size; +} diff --git a/scene/resources/label_settings.h b/scene/resources/label_settings.h index f9998b752a2..82b8966d5d0 100644 --- a/scene/resources/label_settings.h +++ b/scene/resources/label_settings.h @@ -32,12 +32,25 @@ #include "core/io/resource.h" #include "font.h" +#include "scene/property_list_helper.h" /*************************************************************************/ class LabelSettings : public Resource { GDCLASS(LabelSettings, Resource); +public: + struct StackedOutlineData { + int32_t size = 0; + Color color; + }; + struct StackedShadowData { + Vector2i offset = Vector2i(1, 1); + Color color; + int32_t outline_size = 0; + }; + +private: real_t line_spacing = 3; real_t paragraph_spacing = 0; @@ -52,10 +65,34 @@ class LabelSettings : public Resource { Color shadow_color = Color(0, 0, 0, 0); Vector2 shadow_offset = Vector2(1, 1); + Vector stacked_outline_data; + Vector stacked_shadow_data; + + static inline PropertyListHelper stacked_outline_base_property_helper; + static inline PropertyListHelper stacked_shadow_base_property_helper; + PropertyListHelper stacked_outline_property_helper; + PropertyListHelper stacked_shadow_property_helper; + void _font_changed(); protected: static void _bind_methods(); + bool _set(const StringName &p_name, const Variant &p_value) { + return stacked_outline_property_helper.property_set_value(p_name, p_value) || stacked_shadow_property_helper.property_set_value(p_name, p_value); + } + bool _get(const StringName &p_name, Variant &r_ret) const { + return stacked_outline_property_helper.property_get_value(p_name, r_ret) || stacked_shadow_property_helper.property_get_value(p_name, r_ret); + } + void _get_property_list(List *p_list) const { + stacked_outline_property_helper.get_property_list(p_list); + stacked_shadow_property_helper.get_property_list(p_list); + } + bool _property_can_revert(const StringName &p_name) const { + return stacked_outline_property_helper.property_can_revert(p_name) || stacked_shadow_property_helper.property_can_revert(p_name); + } + bool _property_get_revert(const StringName &p_name, Variant &r_property) const { + return stacked_outline_property_helper.property_get_revert(p_name, r_property) || stacked_shadow_property_helper.property_get_revert(p_name, r_property); + } public: void set_line_spacing(real_t p_spacing); @@ -87,4 +124,33 @@ public: void set_shadow_offset(const Vector2 &p_offset); Vector2 get_shadow_offset() const; + + Vector get_stacked_outline_data() const; + int get_stacked_outline_count() const; + void set_stacked_outline_count(int p_count); + void add_stacked_outline(int p_index = -1); + void move_stacked_outline(int p_from_index, int p_to_position); + void remove_stacked_outline(int p_index); + void set_stacked_outline_size(int p_index, int p_size); + int get_stacked_outline_size(int p_index) const; + void set_stacked_outline_color(int p_index, const Color &p_color); + Color get_stacked_outline_color(int p_index) const; + + Vector get_stacked_shadow_data() const; + int get_stacked_shadow_count() const; + void set_stacked_shadow_count(int p_count); + void add_stacked_shadow(int p_index = -1); + void move_stacked_shadow(int p_from_index, int p_to_position); + void remove_stacked_shadow(int p_index); + void set_stacked_shadow_offset(int p_index, const Vector2 &p_offset); + Vector2 get_stacked_shadow_offset(int p_index) const; + void set_stacked_shadow_color(int p_index, const Color &p_color); + Color get_stacked_shadow_color(int p_index) const; + void set_stacked_shadow_outline_size(int p_index, int p_size); + int get_stacked_shadow_outline_size(int p_index) const; + + LabelSettings() { + stacked_outline_property_helper.setup_for_instance(stacked_outline_base_property_helper, this); + stacked_shadow_property_helper.setup_for_instance(stacked_shadow_base_property_helper, this); + } };