/*************************************************************************/ /* rich_text_label.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #ifndef RICH_TEXT_LABEL_H #define RICH_TEXT_LABEL_H #include "rich_text_effect.h" #include "scene/gui/scroll_bar.h" class RichTextLabel : public Control { GDCLASS(RichTextLabel, Control); public: enum Align { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_FILL }; enum ListType { LIST_NUMBERS, LIST_LETTERS, LIST_DOTS }; enum ItemType { ITEM_FRAME, ITEM_TEXT, ITEM_IMAGE, ITEM_NEWLINE, ITEM_FONT, ITEM_COLOR, ITEM_UNDERLINE, ITEM_STRIKETHROUGH, ITEM_ALIGN, ITEM_INDENT, ITEM_LIST, ITEM_TABLE, ITEM_FADE, ITEM_SHAKE, ITEM_WAVE, ITEM_TORNADO, ITEM_RAINBOW, ITEM_META, ITEM_CUSTOMFX }; protected: static void _bind_methods(); private: class Item; struct Line { Item *from; Vector offset_caches; Vector height_caches; Vector ascent_caches; Vector descent_caches; Vector space_caches; int height_cache; int height_accum_cache; int char_count; int minimum_width; int maximum_width; Line() { from = NULL; char_count = 0; } }; class Item : public Object { GDCLASS(Item, Object); public: int index; Item *parent; ItemType type; List subitems; List::Element *E; int line; void _clear_children() { while (subitems.size()) { memdelete(subitems.front()->get()); subitems.pop_front(); } } Item() { parent = NULL; E = NULL; line = 0; } virtual ~Item() { _clear_children(); } }; class ItemFrame : public Item { GDCLASS(ItemFrame, Item); public: int parent_line; bool cell; Vector lines; int first_invalid_line; ItemFrame *parent_frame; ItemFrame() { type = ITEM_FRAME; parent_frame = NULL; cell = false; parent_line = 0; } }; class ItemText : public Item { GDCLASS(ItemText, Item); public: String text; ItemText() { type = ITEM_TEXT; } }; class ItemImage : public Item { GDCLASS(ItemImage, Item); public: Ref image; ItemImage() { type = ITEM_IMAGE; } }; class ItemFont : public Item { GDCLASS(ItemFont, Item); public: Ref font; ItemFont() { type = ITEM_FONT; } }; class ItemColor : public Item { GDCLASS(ItemColor, Item); public: Color color; ItemColor() { type = ITEM_COLOR; } }; class ItemUnderline : public Item { GDCLASS(ItemUnderline, Item); public: ItemUnderline() { type = ITEM_UNDERLINE; } }; class ItemStrikethrough : public Item { GDCLASS(ItemStrikethrough, Item); public: ItemStrikethrough() { type = ITEM_STRIKETHROUGH; } }; class ItemMeta : public Item { GDCLASS(ItemMeta, Item); public: Variant meta; ItemMeta() { type = ITEM_META; } }; class ItemAlign : public Item { GDCLASS(ItemAlign, Item); public: Align align; ItemAlign() { type = ITEM_ALIGN; } }; class ItemIndent : public Item { GDCLASS(ItemIndent, Item); public: int level; ItemIndent() { type = ITEM_INDENT; } }; class ItemList : public Item { GDCLASS(ItemList, Item); public: ListType list_type; ItemList() { type = ITEM_LIST; } }; class ItemNewline : public Item { GDCLASS(ItemNewline, Item); public: ItemNewline() { type = ITEM_NEWLINE; } }; class ItemTable : public Item { GDCLASS(ItemTable, Item); public: struct Column { bool expand; int expand_ratio; int min_width; int max_width; int width; }; Vector columns; int total_width; ItemTable() { type = ITEM_TABLE; } }; class ItemFade : public Item { GDCLASS(ItemFade, Item); public: int starting_index; int length; ItemFade() { type = ITEM_FADE; } }; class ItemFX : public Item { GDCLASS(ItemFX, Item); public: float elapsed_time; ItemFX() { elapsed_time = 0.0f; } }; class ItemShake : public ItemFX { GDCLASS(ItemShake, ItemFX); public: int strength; float rate; uint64_t _current_rng; uint64_t _previous_rng; ItemShake() { strength = 0; rate = 0.0f; _current_rng = 0; type = ITEM_SHAKE; } void reroll_random() { _previous_rng = _current_rng; _current_rng = Math::rand(); } uint64_t offset_random(int index) { return (_current_rng >> (index % 64)) | (_current_rng << (64 - (index % 64))); } uint64_t offset_previous_random(int index) { return (_previous_rng >> (index % 64)) | (_previous_rng << (64 - (index % 64))); } }; class ItemWave : public ItemFX { GDCLASS(ItemWave, ItemFX); public: float frequency; float amplitude; ItemWave() { frequency = 1.0f; amplitude = 1.0f; type = ITEM_WAVE; } }; class ItemTornado : public ItemFX { GDCLASS(ItemTornado, ItemFX); public: float radius; float frequency; ItemTornado() { radius = 1.0f; frequency = 1.0f; type = ITEM_TORNADO; } }; class ItemRainbow : public ItemFX { GDCLASS(ItemRainbow, ItemFX); public: float saturation; float value; float frequency; ItemRainbow() { saturation = 0.8f; value = 0.8f; frequency = 1.0f; type = ITEM_RAINBOW; } }; class ItemCustomFX : public ItemFX { GDCLASS(ItemCustomFX, ItemFX); public: String identifier; Dictionary environment; ItemCustomFX() { identifier = ""; environment = Dictionary(); type = ITEM_CUSTOMFX; } virtual ~ItemCustomFX() { _clear_children(); environment.clear(); } }; ItemFrame *main; Item *current; ItemFrame *current_frame; VScrollBar *vscroll; bool scroll_visible; bool scroll_follow; bool scroll_following; bool scroll_active; int scroll_w; bool scroll_updated; bool updating_scroll; int current_idx; int visible_line_count; int tab_size; bool underline_meta; bool override_selected_font_color; Align default_align; ItemMeta *meta_hovering; Variant current_meta; Vector > custom_effects; void _invalidate_current_line(ItemFrame *p_frame); void _validate_line_caches(ItemFrame *p_frame); void _add_item(Item *p_item, bool p_enter = false, bool p_ensure_newline = false); void _remove_item(Item *p_item, const int p_line, const int p_subitem_line); struct ProcessState { int line_width; }; enum ProcessMode { PROCESS_CACHE, PROCESS_DRAW, PROCESS_POINTER }; struct Selection { Item *click; int click_char; Item *from; int from_char; Item *to; int to_char; bool active; // anything selected? i.e. from, to, etc. valid? bool enabled; // allow selections? }; Selection selection; int visible_characters; float percent_visible; int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL, int p_char_count = 0); void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL); Ref _find_font(Item *p_item); int _find_margin(Item *p_item, const Ref &p_base_font); Align _find_align(Item *p_item); Color _find_color(Item *p_item, const Color &p_default_color); bool _find_underline(Item *p_item); bool _find_strikethrough(Item *p_item); bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = NULL); bool _find_layout_subitem(Item *from, Item *to); bool _find_by_type(Item *p_item, ItemType p_type); template T *_fetch_by_type(Item *p_item, ItemType p_type) { Item *item = p_item; T *result = NULL; while (item) { if (item->type == p_type) { result = Object::cast_to(item); if (result) return result; } item = item->parent; } return result; }; template void _fetch_item_stack(Item *p_item, Vector &r_stack) { Item *item = p_item; while (item) { T *found = Object::cast_to(item); if (found) { r_stack.push_back(found); } item = item->parent; } } void _update_scroll(); void _update_fx(ItemFrame *p_frame, float p_delta_time); void _scroll_changed(double); void _gui_input(Ref p_event); Item *_get_next_item(Item *p_item, bool p_free = false); Item *_get_prev_item(Item *p_item, bool p_free = false); Rect2 _get_text_rect(); Ref _get_custom_effect_by_code(String p_bbcode_identifier); virtual Dictionary parse_expressions_for_values(Vector p_expressions); bool use_bbcode; String bbcode; void _update_all_lines(); int fixed_width; protected: void _notification(int p_what); public: String get_text(); void add_text(const String &p_text); void add_image(const Ref &p_image); void add_newline(); bool remove_line(const int p_line); void push_font(const Ref &p_font); void push_color(const Color &p_color); void push_underline(); void push_strikethrough(); void push_align(Align p_align); void push_indent(int p_level); void push_list(ListType p_list); void push_meta(const Variant &p_meta); void push_table(int p_columns); void push_fade(int p_start_index, int p_length); void push_shake(int p_strength, float p_rate); void push_wave(float p_frequency, float p_amplitude); void push_tornado(float p_frequency, float p_radius); void push_rainbow(float p_saturation, float p_value, float p_frequency); void push_customfx(String p_identifier, Dictionary p_environment); void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1); int get_current_table_column() const; void push_cell(); void pop(); void clear(); void set_offset(int p_pixel); void set_meta_underline(bool p_underline); bool is_meta_underlined() const; void set_override_selected_font_color(bool p_override_selected_font_color); bool is_overriding_selected_font_color() const; void set_scroll_active(bool p_active); bool is_scroll_active() const; void set_scroll_follow(bool p_follow); bool is_scroll_following() const; void set_tab_size(int p_spaces); int get_tab_size() const; bool search(const String &p_string, bool p_from_selection = false, bool p_search_previous = false); void scroll_to_line(int p_line); int get_line_count() const; int get_visible_line_count() const; int get_content_height(); VScrollBar *get_v_scroll() { return vscroll; } virtual CursorShape get_cursor_shape(const Point2 &p_pos) const; void set_selection_enabled(bool p_enabled); bool is_selection_enabled() const; void selection_copy(); Error parse_bbcode(const String &p_bbcode); Error append_bbcode(const String &p_bbcode); void set_use_bbcode(bool p_enable); bool is_using_bbcode() const; void set_bbcode(const String &p_bbcode); String get_bbcode() const; void set_text(const String &p_string); void set_visible_characters(int p_visible); int get_visible_characters() const; int get_total_character_count() const; void set_percent_visible(float p_percent); float get_percent_visible() const; void set_effects(const Vector &effects); Vector get_effects(); void install_effect(const Variant effect); void set_fixed_size_to_width(int p_width); virtual Size2 get_minimum_size() const; RichTextLabel(); ~RichTextLabel(); }; VARIANT_ENUM_CAST(RichTextLabel::Align); VARIANT_ENUM_CAST(RichTextLabel::ListType); VARIANT_ENUM_CAST(RichTextLabel::ItemType); #endif // RICH_TEXT_LABEL_H