diff options
Diffstat (limited to 'scene/gui')
-rw-r--r-- | scene/gui/color_picker.cpp | 18 | ||||
-rw-r--r-- | scene/gui/control.cpp | 1 | ||||
-rw-r--r-- | scene/gui/grid_container.cpp | 35 | ||||
-rw-r--r-- | scene/gui/line_edit.cpp | 85 | ||||
-rw-r--r-- | scene/gui/line_edit.h | 3 | ||||
-rw-r--r-- | scene/gui/option_button.cpp | 6 | ||||
-rw-r--r-- | scene/gui/rich_text_effect.cpp | 5 | ||||
-rw-r--r-- | scene/gui/rich_text_effect.h | 2 | ||||
-rw-r--r-- | scene/gui/rich_text_label.cpp | 85 | ||||
-rw-r--r-- | scene/gui/rich_text_label.h | 59 | ||||
-rw-r--r-- | scene/gui/slider.cpp | 1 | ||||
-rw-r--r-- | scene/gui/tab_container.cpp | 112 | ||||
-rw-r--r-- | scene/gui/tab_container.h | 4 | ||||
-rw-r--r-- | scene/gui/tabs.cpp | 18 | ||||
-rw-r--r-- | scene/gui/tabs.h | 4 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 10 | ||||
-rw-r--r-- | scene/gui/tree.cpp | 10 | ||||
-rw-r--r-- | scene/gui/tree.h | 1 |
18 files changed, 301 insertions, 158 deletions
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 6dd9e401f6..ffe011e5f7 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -396,11 +396,18 @@ void ColorPicker::_update_text_value() { } void ColorPicker::_sample_draw() { - Rect2 r = Rect2(Point2(), Size2(uv_edit->get_size().width, sample->get_size().height * 0.95)); + const Rect2 r = Rect2(Point2(), Size2(uv_edit->get_size().width, sample->get_size().height * 0.95)); + if (color.a < 1.0) { sample->draw_texture_rect(get_icon("preset_bg", "ColorPicker"), r, true); } + sample->draw_rect(r, color); + + if (color.r > 1 || color.g > 1 || color.b > 1) { + // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview + sample->draw_texture(get_icon("overbright_indicator", "ColorPicker"), Point2()); + } } void ColorPicker::_hsv_draw(int p_which, Control *c) { @@ -894,10 +901,15 @@ void ColorPickerButton::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { - Ref<StyleBox> normal = get_stylebox("normal"); - Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size()); + const Ref<StyleBox> normal = get_stylebox("normal"); + const Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size()); draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true); draw_rect(r, color); + + if (color.r > 1 || color.g > 1 || color.b > 1) { + // Draw an indicator to denote that the color is "overbright" and can't be displayed accurately in the preview + draw_texture(Control::get_icon("overbright_indicator", "ColorPicker"), normal->get_offset()); + } } break; case MainLoop::NOTIFICATION_WM_QUIT_REQUEST: { diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index d39f017cad..f8f29632b3 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -645,6 +645,7 @@ void Control::_notification(int p_notification) { } break; case NOTIFICATION_THEME_CHANGED: { + minimum_size_changed(); update(); } break; case NOTIFICATION_MODAL_CLOSE: { diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index d0e2edc7b5..a6842603fd 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -36,20 +36,18 @@ void GridContainer::_notification(int p_what) { case NOTIFICATION_SORT_CHILDREN: { - int valid_controls_index; - - Map<int, int> col_minw; // max of min_width of all controls in each col (indexed by col) - Map<int, int> row_minh; // max of min_height of all controls in each row (indexed by row) - Set<int> col_expanded; // columns which have the SIZE_EXPAND flag set - Set<int> row_expanded; // rows which have the SIZE_EXPAND flag set + Map<int, int> col_minw; // Max of min_width of all controls in each col (indexed by col). + Map<int, int> row_minh; // Max of min_height of all controls in each row (indexed by row). + Set<int> col_expanded; // Columns which have the SIZE_EXPAND flag set. + Set<int> row_expanded; // Rows which have the SIZE_EXPAND flag set. int hsep = get_constant("hseparation"); int vsep = get_constant("vseparation"); int max_col = MIN(get_child_count(), columns); int max_row = get_child_count() / columns; - // Compute the per-column/per-row data - valid_controls_index = 0; + // Compute the per-column/per-row data. + int valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { Control *c = Object::cast_to<Control>(get_child(i)); if (!c || !c->is_visible_in_tree()) @@ -77,7 +75,12 @@ void GridContainer::_notification(int p_what) { } } - // Evaluate the remaining space for expanded columns/rows + // Consider all empty columns expanded. + for (int i = valid_controls_index; i < columns; i++) { + col_expanded.insert(i); + } + + // Evaluate the remaining space for expanded columns/rows. Size2 remaining_space = get_size(); for (Map<int, int>::Element *E = col_minw.front(); E; E = E->next()) { if (!col_expanded.has(E->key())) @@ -93,7 +96,7 @@ void GridContainer::_notification(int p_what) { bool can_fit = false; while (!can_fit && col_expanded.size() > 0) { - // Check if all minwidth constraints are ok if we use the remaining space + // Check if all minwidth constraints are OK if we use the remaining space. can_fit = true; int max_index = col_expanded.front()->get(); for (Set<int>::Element *E = col_expanded.front(); E; E = E->next()) { @@ -105,7 +108,7 @@ void GridContainer::_notification(int p_what) { } } - // If not, the column with maximum minwidth is not expanded + // If not, the column with maximum minwidth is not expanded. if (!can_fit) { col_expanded.erase(max_index); remaining_space.width -= col_minw[max_index]; @@ -114,7 +117,7 @@ void GridContainer::_notification(int p_what) { can_fit = false; while (!can_fit && row_expanded.size() > 0) { - // Check if all minwidth constraints are ok if we use the remaining space + // Check if all minheight constraints are OK if we use the remaining space. can_fit = true; int max_index = row_expanded.front()->get(); for (Set<int>::Element *E = row_expanded.front(); E; E = E->next()) { @@ -126,14 +129,14 @@ void GridContainer::_notification(int p_what) { } } - // If not, the row with maximum minwidth is not expanded + // If not, the row with maximum minheight is not expanded. if (!can_fit) { row_expanded.erase(max_index); remaining_space.height -= row_minh[max_index]; } } - // Finally, fit the nodes + // Finally, fit the nodes. int col_expand = col_expanded.size() > 0 ? remaining_space.width / col_expanded.size() : 0; int row_expand = row_expanded.size() > 0 ? remaining_space.height / row_expanded.size() : 0; @@ -152,11 +155,11 @@ void GridContainer::_notification(int p_what) { if (col == 0) { col_ofs = 0; if (row > 0) - row_ofs += ((row_expanded.has(row - 1)) ? row_expand : row_minh[row - 1]) + vsep; + row_ofs += (row_expanded.has(row - 1) ? row_expand : row_minh[row - 1]) + vsep; } Point2 p(col_ofs, row_ofs); - Size2 s((col_expanded.has(col)) ? col_expand : col_minw[col], (row_expanded.has(row)) ? row_expand : row_minh[row]); + Size2 s(col_expanded.has(col) ? col_expand : col_minw[col], row_expanded.has(row) ? row_expand : row_minh[row]); fit_child_in_rect(c, Rect2(p, s)); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index c2959b3a72..ab6f80bfa9 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -201,7 +201,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { switch (code) { - case (KEY_X): { // CUT + case (KEY_X): { // CUT. if (editable) { cut_text(); @@ -209,12 +209,13 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } break; - case (KEY_C): { // COPY + case (KEY_C): { // COPY. + copy_text(); } break; - case (KEY_V): { // PASTE + case (KEY_V): { // PASTE. if (editable) { @@ -223,7 +224,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } break; - case (KEY_Z): { // undo / redo + case (KEY_Z): { // Undo/redo. if (editable) { if (k->get_shift()) { redo(); @@ -233,7 +234,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } break; - case (KEY_U): { // Delete from start to cursor + case (KEY_U): { // Delete from start to cursor. if (editable) { @@ -254,7 +255,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } break; - case (KEY_Y): { // PASTE (Yank for unix users) + case (KEY_Y): { // PASTE (Yank for unix users). if (editable) { @@ -262,7 +263,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } break; - case (KEY_K): { // Delete from cursor_pos to end + case (KEY_K): { // Delete from cursor_pos to end. if (editable) { @@ -272,15 +273,15 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { } } break; - case (KEY_A): { //Select All + case (KEY_A): { // Select all. select(); } break; #ifdef APPLE_STYLE_KEYS - case (KEY_LEFT): { // Go to start of text - like HOME key + case (KEY_LEFT): { // Go to start of text - like HOME key. set_cursor_position(0); } break; - case (KEY_RIGHT): { // Go to end of text - like END key + case (KEY_RIGHT): { // Go to end of text - like END key. set_cursor_position(text.length()); } break; #endif @@ -473,7 +474,7 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { int text_len = text.length(); if (cursor_pos == text_len) - break; // nothing to do + break; // Nothing to do. #ifdef APPLE_STYLE_KEYS if (k->get_alt()) { @@ -740,7 +741,7 @@ void LineEdit::_notification(int p_what) { Color cursor_color = get_color("cursor_color"); const String &t = using_placeholder ? placeholder_translated : text; - // draw placeholder color + // Draw placeholder color. if (using_placeholder) font_color.a *= placeholder_alpha; @@ -755,6 +756,7 @@ void LineEdit::_notification(int p_what) { color_icon = get_color("clear_button_color"); } } + r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(MARGIN_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon); if (align == ALIGN_CENTER) { @@ -772,7 +774,7 @@ void LineEdit::_notification(int p_what) { FontDrawer drawer(font, Color(1, 1, 1)); while (true) { - //end of string, break! + // End of string, break. if (char_ofs >= t.length()) break; @@ -809,7 +811,7 @@ void LineEdit::_notification(int p_what) { CharType next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1]; int char_width = font->get_char_size(cchar, next).width; - // end of widget, break! + // End of widget, break. if ((x_ofs + char_width) > ofs_max) break; @@ -864,7 +866,7 @@ void LineEdit::_notification(int p_what) { } } - if (char_ofs == cursor_pos && draw_caret) { //may be at the end + if (char_ofs == cursor_pos && draw_caret) { // May be at the end. if (ime_text.length() == 0) { #ifdef TOOLS_ENABLED VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(Math::round(EDSCALE), caret_height)), cursor_color); @@ -1053,7 +1055,7 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) { } pixel_ofs += char_w; - if (pixel_ofs > p_x) { //found what we look for + if (pixel_ofs > p_x) { // Found what we look for. break; } @@ -1266,10 +1268,10 @@ void LineEdit::set_cursor_position(int p_pos) { Ref<Font> font = get_font("font"); if (cursor_pos <= window_pos) { - /* Adjust window if cursor goes too much to the left */ + // Adjust window if cursor goes too much to the left. set_window_pos(MAX(0, cursor_pos - 1)); } else { - /* Adjust window if cursor goes too much to the right */ + // Adjust window if cursor goes too much to the right. int window_width = get_size().width - style->get_minimum_size().width; bool display_clear_icon = !text.empty() && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { @@ -1288,10 +1290,10 @@ void LineEdit::set_cursor_position(int p_pos) { for (int i = cursor_pos; i >= window_pos; i--) { if (i >= text.length()) { - //do not do this, because if the cursor is at the end, its just fine that it takes no space - //accum_width = font->get_char_size(' ').width; //anything should do + // Do not do this, because if the cursor is at the end, its just fine that it takes no space. + // accum_width = font->get_char_size(' ').width; } else { - accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; //anything should do + accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; // Anything should do. } if (accum_width > window_width) break; @@ -1353,23 +1355,31 @@ Size2 LineEdit::get_minimum_size() const { Ref<StyleBox> style = get_stylebox("normal"); Ref<Font> font = get_font("font"); - Size2 min = style->get_minimum_size(); - min.height += font->get_height(); + Size2 min_size; - //minimum size of text + // Minimum size of text. int space_size = font->get_char_size(' ').x; - int mstext = get_constant("minimum_spaces") * space_size; + min_size.width = get_constant("minimum_spaces") * space_size; if (expand_to_text_length) { - mstext = MAX(mstext, font->get_string_size(text).x + space_size); //add a spce because some fonts are too exact, and because cursor needs a bit more when at the end + // Add a space because some fonts are too exact, and because cursor needs a bit more when at the end. + min_size.width = MAX(min_size.width, font->get_string_size(text).x + space_size); } - min.width += mstext; + min_size.height = font->get_height(); - return min; -} + // Take icons into account. + if (!text.empty() && is_editable() && clear_button_enabled) { + min_size.width = MAX(min_size.width, Control::get_icon("clear")->get_width()); + min_size.height = MAX(min_size.height, Control::get_icon("clear")->get_height()); + } + if (right_icon.is_valid()) { + min_size.width = MAX(min_size.width, right_icon->get_width()); + min_size.height = MAX(min_size.height, right_icon->get_height()); + } -/* selection */ + return style->get_minimum_size() + min_size; +} void LineEdit::deselect() { @@ -1460,8 +1470,8 @@ bool LineEdit::is_secret() const { void LineEdit::set_secret_character(const String &p_string) { - // An empty string as the secret character would crash the engine - // It also wouldn't make sense to use multiple characters as the secret character + // An empty string as the secret character would crash the engine. + // It also wouldn't make sense to use multiple characters as the secret character. ERR_FAIL_COND_MSG(p_string.length() != 1, "Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)."); secret_character = p_string; @@ -1613,8 +1623,11 @@ void LineEdit::set_right_icon(const Ref<Texture> &p_icon) { update(); } -void LineEdit::_text_changed() { +Ref<Texture> LineEdit::get_right_icon() { + return right_icon; +} +void LineEdit::_text_changed() { if (expand_to_text_length) minimum_size_changed(); @@ -1735,6 +1748,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_shortcut_keys_enabled"), &LineEdit::is_shortcut_keys_enabled); ClassDB::bind_method(D_METHOD("set_selecting_enabled", "enable"), &LineEdit::set_selecting_enabled); ClassDB::bind_method(D_METHOD("is_selecting_enabled"), &LineEdit::is_selecting_enabled); + ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon); + ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon); ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); ADD_SIGNAL(MethodInfo("text_entered", PropertyInfo(Variant::STRING, "new_text"))); @@ -1760,11 +1775,11 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_button_enabled"), "set_clear_button_enabled", "is_clear_button_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon"); ADD_GROUP("Placeholder", "placeholder_"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha"); @@ -1811,7 +1826,7 @@ LineEdit::LineEdit() { context_menu_enabled = true; menu = memnew(PopupMenu); add_child(menu); - editable = false; // initialise to opposite first, so we get past the early-out in set_editable + editable = false; // Initialise to opposite first, so we get past the early-out in set_editable. set_editable(true); menu->connect("id_pressed", this, "menu_option"); expand_to_text_length = false; diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 9356840163..3424131dad 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -82,7 +82,7 @@ private: int cursor_pos; int window_pos; - int max_length; // 0 for no maximum + int max_length; // 0 for no maximum. int cached_width; int cached_placeholder_width; @@ -230,6 +230,7 @@ public: bool is_selecting_enabled() const; void set_right_icon(const Ref<Texture> &p_icon); + Ref<Texture> get_right_icon(); virtual bool is_text_field() const; LineEdit(); diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index d1840e43a3..de8df4215d 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -116,10 +116,16 @@ void OptionButton::add_item(const String &p_label, int p_id) { void OptionButton::set_item_text(int p_idx, const String &p_text) { popup->set_item_text(p_idx, p_text); + + if (current == p_idx) + set_text(p_text); } void OptionButton::set_item_icon(int p_idx, const Ref<Texture> &p_icon) { popup->set_item_icon(p_idx, p_icon); + + if (current == p_idx) + set_icon(p_icon); } void OptionButton::set_item_id(int p_idx, int p_id) { diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp index 67fa85b832..f9e0be5b31 100644 --- a/scene/gui/rich_text_effect.cpp +++ b/scene/gui/rich_text_effect.cpp @@ -119,4 +119,9 @@ CharFXTransform::CharFXTransform() { offset = Point2(); color = Color(); character = 0; + elapsed_time = 0.0f; +} + +CharFXTransform::~CharFXTransform() { + environment.clear(); } diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h index f9c3e15399..4330cebfe6 100644 --- a/scene/gui/rich_text_effect.h +++ b/scene/gui/rich_text_effect.h @@ -64,6 +64,8 @@ public: Dictionary environment; CharFXTransform(); + ~CharFXTransform(); + uint64_t get_relative_index() { return relative_index; } void set_relative_index(uint64_t p_index) { relative_index = p_index; } uint64_t get_absolute_index() { return absolute_index; } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index d9ae42d6e6..c5330c78e1 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -348,6 +348,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int it_char_start = p_char_count; Vector<ItemFX *> fx_stack = Vector<ItemFX *>(); + _fetch_item_fx_stack(text, fx_stack); bool custom_fx_ok = true; if (p_mode == PROCESS_DRAW) { @@ -359,8 +360,14 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & strikethrough = true; } - fade = _fetch_by_type<ItemFade>(text, ITEM_FADE); - _fetch_item_stack<ItemFX>(text, fx_stack); + Item *fade_item = it; + while (fade_item) { + if (fade_item->type == ITEM_FADE) { + fade = static_cast<ItemFade *>(fade_item); + break; + } + fade_item = fade_item->parent; + } } else if (p_mode == PROCESS_CACHE) { l.char_count += text->text.length(); @@ -467,18 +474,16 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & faded_visibility > 0.0f); for (int j = 0; j < fx_stack.size(); j++) { - ItemCustomFX *item_custom = Object::cast_to<ItemCustomFX>(fx_stack[j]); - ItemShake *item_shake = Object::cast_to<ItemShake>(fx_stack[j]); - ItemWave *item_wave = Object::cast_to<ItemWave>(fx_stack[j]); - ItemTornado *item_tornado = Object::cast_to<ItemTornado>(fx_stack[j]); - ItemRainbow *item_rainbow = Object::cast_to<ItemRainbow>(fx_stack[j]); - - if (item_custom && custom_fx_ok) { - Ref<CharFXTransform> charfx = Ref<CharFXTransform>(memnew(CharFXTransform)); - Ref<RichTextEffect> custom_effect = _get_custom_effect_by_code(item_custom->identifier); + ItemFX *item_fx = fx_stack[j]; + + if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) { + ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx); + + Ref<CharFXTransform> charfx = item_custom->char_fx_transform; + Ref<RichTextEffect> custom_effect = item_custom->custom_effect; + if (!custom_effect.is_null()) { charfx->elapsed_time = item_custom->elapsed_time; - charfx->environment = item_custom->environment; charfx->relative_index = c_item_offset; charfx->absolute_index = p_char_count; charfx->visibility = visible; @@ -494,7 +499,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & visible &= charfx->visibility; fx_char = charfx->character; } - } else if (item_shake) { + } else if (item_fx->type == ITEM_SHAKE) { + ItemShake *item_shake = static_cast<ItemShake *>(item_fx); + uint64_t char_current_rand = item_shake->offset_random(c_item_offset); uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset); uint64_t max_rand = 2147483647; @@ -509,14 +516,20 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f; - } else if (item_wave) { + } else if (item_fx->type == ITEM_WAVE) { + ItemWave *item_wave = static_cast<ItemWave *>(item_fx); + double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f); fx_offset += Point2(0, 1) * value; - } else if (item_tornado) { + } else if (item_fx->type == ITEM_TORNADO) { + ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx); + double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); fx_offset += Point2(torn_x, torn_y); - } else if (item_rainbow) { + } else if (item_fx->type == ITEM_RAINBOW) { + ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx); + fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)), item_rainbow->saturation, item_rainbow->value, @@ -884,7 +897,11 @@ void RichTextLabel::_update_scroll() { void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, float p_delta_time) { Item *it = p_frame; while (it) { - ItemFX *ifx = Object::cast_to<ItemFX>(it); + ItemFX *ifx = NULL; + + if (it->type == ITEM_CUSTOMFX || it->type == ITEM_SHAKE || it->type == ITEM_WAVE || it->type == ITEM_TORNADO || it->type == ITEM_RAINBOW) { + ifx = static_cast<ItemFX *>(it); + } if (!ifx) { it = _get_next_item(it, true); @@ -893,7 +910,12 @@ void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, float p_delta_ ifx->elapsed_time += p_delta_time; - ItemShake *shake = Object::cast_to<ItemShake>(it); + ItemShake *shake = NULL; + + if (it->type == ITEM_SHAKE) { + shake = static_cast<ItemShake *>(it); + } + if (shake) { bool cycle = (shake->elapsed_time > (1.0f / shake->rate)); if (cycle) { @@ -983,9 +1005,6 @@ void RichTextLabel::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { float dt = get_process_delta_time(); - for (int i = 0; i < custom_effects.size(); i++) { - } - _update_fx(main, dt); update(); } @@ -1408,6 +1427,17 @@ bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) { return false; } +void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack) { + Item *item = p_item; + while (item) { + if (item->type == ITEM_CUSTOMFX || item->type == ITEM_SHAKE || item->type == ITEM_WAVE || item->type == ITEM_TORNADO || item->type == ITEM_RAINBOW) { + r_stack.push_back(static_cast<ItemFX *>(item)); + } + + item = item->parent; + } +} + bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { Item *item = p_item; @@ -1776,10 +1806,10 @@ void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_freq _add_item(item, true); } -void RichTextLabel::push_customfx(String p_identifier, Dictionary p_environment) { +void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionary p_environment) { ItemCustomFX *item = memnew(ItemCustomFX); - item->identifier = p_identifier; - item->environment = p_environment; + item->custom_effect = p_custom_effect; + item->char_fx_transform->environment = p_environment; _add_item(item, true); } @@ -2287,7 +2317,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { Ref<RichTextEffect> effect = _get_custom_effect_by_code(identifier); if (!effect.is_null()) { - push_customfx(identifier, properties); + push_customfx(effect, properties); pos = brk_end + 1; tag_stack.push_front(identifier); set_process_internal(true); @@ -2700,17 +2730,16 @@ Size2 RichTextLabel::get_minimum_size() const { } Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) { - Ref<RichTextEffect> r; for (int i = 0; i < custom_effects.size(); i++) { if (!custom_effects[i].is_valid()) continue; if (custom_effects[i]->get_bbcode() == p_bbcode_identifier) { - r = custom_effects[i]; + return custom_effects[i]; } } - return r; + return Ref<RichTextEffect>(); } Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) { diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 6755f9ef2a..1c90d974e4 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -103,8 +103,7 @@ private: } }; - struct Item : public Object { - + struct Item { int index; Item *parent; ItemType type; @@ -128,7 +127,6 @@ private: }; struct ItemFrame : public Item { - int parent_line; bool cell; Vector<Line> lines; @@ -144,70 +142,58 @@ private: }; struct ItemText : public Item { - String text; ItemText() { type = ITEM_TEXT; } }; struct ItemImage : public Item { - Ref<Texture> image; ItemImage() { type = ITEM_IMAGE; } }; struct ItemFont : public Item { - Ref<Font> font; ItemFont() { type = ITEM_FONT; } }; struct ItemColor : public Item { - Color color; ItemColor() { type = ITEM_COLOR; } }; struct ItemUnderline : public Item { - ItemUnderline() { type = ITEM_UNDERLINE; } }; struct ItemStrikethrough : public Item { - ItemStrikethrough() { type = ITEM_STRIKETHROUGH; } }; struct ItemMeta : public Item { - Variant meta; ItemMeta() { type = ITEM_META; } }; struct ItemAlign : public Item { - Align align; ItemAlign() { type = ITEM_ALIGN; } }; struct ItemIndent : public Item { - int level; ItemIndent() { type = ITEM_INDENT; } }; struct ItemList : public Item { - ListType list_type; ItemList() { type = ITEM_LIST; } }; struct ItemNewline : public Item { - ItemNewline() { type = ITEM_NEWLINE; } }; struct ItemTable : public Item { - struct Column { bool expand; int expand_ratio; @@ -301,18 +287,20 @@ private: }; struct ItemCustomFX : public ItemFX { - String identifier; - Dictionary environment; + Ref<CharFXTransform> char_fx_transform; + Ref<RichTextEffect> custom_effect; ItemCustomFX() { - identifier = ""; - environment = Dictionary(); type = ITEM_CUSTOMFX; + + char_fx_transform.instance(); } virtual ~ItemCustomFX() { _clear_children(); - environment.clear(); + + char_fx_transform.unref(); + custom_effect.unref(); } }; @@ -391,32 +379,7 @@ private: 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 <typename T> - 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<T>(item); - if (result) - return result; - } - item = item->parent; - } - - return result; - }; - template <typename T> - void _fetch_item_stack(Item *p_item, Vector<T *> &r_stack) { - Item *item = p_item; - while (item) { - T *found = Object::cast_to<T>(item); - if (found) { - r_stack.push_back(found); - } - item = item->parent; - } - } + void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack); void _update_scroll(); void _update_fx(ItemFrame *p_frame, float p_delta_time); @@ -456,11 +419,11 @@ public: 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_level, float p_rate); + 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 push_customfx(Ref<RichTextEffect> p_custom_effect, 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(); diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index b777e77bc3..9f853cf0c8 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -287,7 +287,6 @@ void Slider::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrollable"), "set_scrollable", "is_scrollable"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tick_count", PROPERTY_HINT_RANGE, "0,4096,1"), "set_ticks", "get_ticks"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ticks_on_borders"), "set_ticks_on_borders", "get_ticks_on_borders"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode"); } Slider::Slider(Orientation p_orientation) { diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 292d80be9d..a29ba36bad 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -94,7 +94,7 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { return; } - // Do not activate tabs when tabs is empty + // Do not activate tabs when tabs is empty. if (get_tab_count() == 0) return; @@ -140,6 +140,76 @@ void TabContainer::_gui_input(const Ref<InputEvent> &p_event) { pos.x -= tab_width; } } + + Ref<InputEventMouseMotion> mm = p_event; + + if (mm.is_valid()) { + + Point2 pos(mm->get_position().x, mm->get_position().y); + Size2 size = get_size(); + + // Mouse must be on tabs in the tab header area. + if (pos.x < tabs_ofs_cache || pos.y > _get_top_margin()) { + + if (menu_hovered || highlight_arrow > -1) { + menu_hovered = false; + highlight_arrow = -1; + update(); + } + return; + } + + Ref<Texture> menu = get_icon("menu"); + if (popup) { + + if (pos.x >= size.width - menu->get_width()) { + if (!menu_hovered) { + menu_hovered = true; + highlight_arrow = -1; + update(); + return; + } + } else if (menu_hovered) { + menu_hovered = false; + update(); + } + + if (menu_hovered) { + return; + } + } + + // Do not activate tabs when tabs is empty. + if ((get_tab_count() == 0 || !buttons_visible_cache) && menu_hovered) { + highlight_arrow = -1; + update(); + return; + } + + int popup_ofs = 0; + if (popup) { + popup_ofs = menu->get_width(); + } + + Ref<Texture> increment = get_icon("increment"); + Ref<Texture> decrement = get_icon("decrement"); + if (pos.x >= size.width - increment->get_width() - popup_ofs) { + + if (highlight_arrow != 1) { + highlight_arrow = 1; + update(); + } + } else if (pos.x >= size.width - increment->get_width() - decrement->get_width() - popup_ofs) { + + if (highlight_arrow != 0) { + highlight_arrow = 0; + update(); + } + } else if (highlight_arrow > -1) { + highlight_arrow = -1; + update(); + } + } } void TabContainer::_notification(int p_what) { @@ -203,9 +273,11 @@ void TabContainer::_notification(int p_what) { Ref<StyleBox> tab_fg = get_stylebox("tab_fg"); Ref<StyleBox> tab_disabled = get_stylebox("tab_disabled"); Ref<Texture> increment = get_icon("increment"); + Ref<Texture> increment_hl = get_icon("increment_highlight"); Ref<Texture> decrement = get_icon("decrement"); + Ref<Texture> decrement_hl = get_icon("decrement_highlight"); Ref<Texture> menu = get_icon("menu"); - Ref<Texture> menu_hl = get_icon("menu_hl"); + Ref<Texture> menu_hl = get_icon("menu_highlight"); Ref<Font> font = get_font("font"); Color font_color_fg = get_color("font_color_fg"); Color font_color_bg = get_color("font_color_bg"); @@ -332,7 +404,7 @@ void TabContainer::_notification(int p_what) { x = get_size().width; if (popup) { x -= menu->get_width(); - if (mouse_x_cache > x) + if (menu_hovered) menu_hl->draw(get_canvas_item(), Size2(x, (header_height - menu_hl->get_height()) / 2)); else menu->draw(get_canvas_item(), Size2(x, (header_height - menu->get_height()) / 2)); @@ -340,23 +412,26 @@ void TabContainer::_notification(int p_what) { // Draw the navigation buttons. if (buttons_visible_cache) { - int y_center = header_height / 2; x -= increment->get_width(); - increment->draw(canvas, - Point2(x, y_center - (increment->get_height() / 2)), - Color(1, 1, 1, last_tab_cache < tabs.size() - 1 ? 1.0 : 0.5)); + if (last_tab_cache < tabs.size() - 1) { + draw_texture(highlight_arrow == 1 ? increment_hl : increment, Point2(x, (header_height - increment->get_height()) / 2)); + } else { + draw_texture(increment, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5)); + } x -= decrement->get_width(); - decrement->draw(canvas, - Point2(x, y_center - (decrement->get_height() / 2)), - Color(1, 1, 1, first_tab_cache > 0 ? 1.0 : 0.5)); + if (first_tab_cache > 0) { + draw_texture(highlight_arrow == 0 ? decrement_hl : decrement, Point2(x, (header_height - decrement->get_height()) / 2)); + } else { + draw_texture(decrement, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5)); + } } } break; case NOTIFICATION_THEME_CHANGED: { minimum_size_changed(); - call_deferred("_on_theme_changed"); //wait until all changed theme + call_deferred("_on_theme_changed"); // Wait until all changed theme. } break; } } @@ -367,6 +442,14 @@ void TabContainer::_on_theme_changed() { } } +void TabContainer::_on_mouse_exited() { + if (menu_hovered || highlight_arrow > -1) { + menu_hovered = false; + highlight_arrow = -1; + update(); + } +} + int TabContainer::_get_tab_width(int p_index) const { ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0); @@ -894,6 +977,7 @@ void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) { bool TabContainer::get_use_hidden_tabs_for_min_size() const { return use_hidden_tabs_for_min_size; } + void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &TabContainer::_gui_input); @@ -925,6 +1009,7 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback); ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed); + ClassDB::bind_method(D_METHOD("_on_mouse_exited"), &TabContainer::_on_mouse_exited); ClassDB::bind_method(D_METHOD("_update_current_tab"), &TabContainer::_update_current_tab); ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); @@ -947,14 +1032,17 @@ TabContainer::TabContainer() { first_tab_cache = 0; last_tab_cache = 0; buttons_visible_cache = false; + menu_hovered = false; + highlight_arrow = -1; tabs_ofs_cache = 0; current = 0; previous = 0; - mouse_x_cache = 0; align = ALIGN_CENTER; tabs_visible = true; popup = NULL; drag_to_rearrange_enabled = false; tabs_rearrange_group = -1; use_hidden_tabs_for_min_size = false; + + connect("mouse_exited", this, "_on_mouse_exited"); } diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 0314f86837..0c17ebc3ae 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -46,7 +46,6 @@ public: }; private: - int mouse_x_cache; int first_tab_cache; int tabs_ofs_cache; int last_tab_cache; @@ -54,6 +53,8 @@ private: int previous; bool tabs_visible; bool buttons_visible_cache; + bool menu_hovered; + int highlight_arrow; TabAlign align; Control *_get_tab(int p_idx) const; int _get_top_margin() const; @@ -65,6 +66,7 @@ private: Vector<Control *> _get_tabs() const; int _get_tab_width(int p_index) const; void _on_theme_changed(); + void _on_mouse_exited(); void _update_current_tab(); protected: diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 7b0836cd28..93b091e8d0 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -226,12 +226,6 @@ void Tabs::_notification(int p_what) { minimum_size_changed(); update(); } break; - case NOTIFICATION_MOUSE_EXIT: { - rb_hover = -1; - cb_hover = -1; - hover = -1; - update(); - } break; case NOTIFICATION_RESIZED: { _update_cache(); _ensure_no_over_offset(); @@ -597,6 +591,15 @@ void Tabs::_update_cache() { } } +void Tabs::_on_mouse_exited() { + + rb_hover = -1; + cb_hover = -1; + hover = -1; + highlight_arrow = -1; + update(); +} + void Tabs::add_tab(const String &p_str, const Ref<Texture> &p_icon) { Tab t; @@ -948,6 +951,7 @@ void Tabs::_bind_methods() { ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input); ClassDB::bind_method(D_METHOD("_update_hover"), &Tabs::_update_hover); + ClassDB::bind_method(D_METHOD("_on_mouse_exited"), &Tabs::_on_mouse_exited); ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count); ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab); ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab); @@ -1024,4 +1028,6 @@ Tabs::Tabs() { hover = -1; drag_to_rearrange_enabled = false; tabs_rearrange_group = -1; + + connect("mouse_exited", this, "_on_mouse_exited"); } diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index 7c54f1acf2..a762b5b9cb 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -89,7 +89,7 @@ private: bool cb_pressing; CloseButtonDisplayPolicy cb_displaypolicy; - int hover; // hovered tab + int hover; // Hovered tab. int min_width; bool scrolling_enabled; bool drag_to_rearrange_enabled; @@ -101,6 +101,8 @@ private: void _update_hover(); void _update_cache(); + void _on_mouse_exited(); + protected: void _gui_input(const Ref<InputEvent> &p_event); void _notification(int p_what); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 7227942ccb..e2bb4e3e91 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -2455,7 +2455,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (mm.is_valid()) { if (select_identifiers_enabled) { - if (mm->get_command() && mm->get_button_mask() == 0) { + if (!dragging_minimap && !dragging_selection && mm->get_command() && mm->get_button_mask() == 0) { String new_word = get_word_at_pos(mm->get_position()); if (new_word != highlighted_word) { @@ -2513,7 +2513,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { #endif if (select_identifiers_enabled) { - if (k->is_pressed()) { + if (k->is_pressed() && !dragging_minimap && !dragging_selection) { highlighted_word = get_word_at_pos(get_local_mouse_position()); update(); @@ -3965,7 +3965,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) { - if (!setting_text) + if (!setting_text && idle_detect->is_inside_tree()) idle_detect->start(); if (undo_enabled) { @@ -4019,7 +4019,7 @@ void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r void TextEdit::_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) { - if (!setting_text) + if (!setting_text && idle_detect->is_inside_tree()) idle_detect->start(); String text; @@ -6110,7 +6110,7 @@ bool TextEdit::is_indent_using_spaces() const { } void TextEdit::set_indent_size(const int p_size) { - ERR_FAIL_COND(p_size <= 0); + ERR_FAIL_COND_MSG(p_size <= 0, "Indend size must be greater than 0."); indent_size = p_size; text.set_indent_size(p_size); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 57663bbe82..2fc4be6900 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -547,6 +547,11 @@ Ref<Texture> TreeItem::get_button(int p_column, int p_idx) const { ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), Ref<Texture>()); return cells[p_column].buttons[p_idx].texture; } +String TreeItem::get_button_tooltip(int p_column, int p_idx) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), String()); + ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), String()); + return cells[p_column].buttons[p_idx].tooltip; +} int TreeItem::get_button_id(int p_column, int p_idx) const { ERR_FAIL_INDEX_V(p_column, cells.size(), -1); ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), -1); @@ -795,6 +800,7 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("add_button", "column", "button", "button_idx", "disabled", "tooltip"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL("")); ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count); + ClassDB::bind_method(D_METHOD("get_button_tooltip", "column", "button_idx"), &TreeItem::get_button_tooltip); ClassDB::bind_method(D_METHOD("get_button", "column", "button_idx"), &TreeItem::get_button); ClassDB::bind_method(D_METHOD("set_button", "column", "button_idx", "button"), &TreeItem::set_button); ClassDB::bind_method(D_METHOD("erase_button", "column", "button_idx"), &TreeItem::erase_button); @@ -2546,7 +2552,9 @@ void Tree::_gui_input(Ref<InputEvent> p_event) { } else { Rect2 rect = get_selected()->get_meta("__focus_rect"); if (rect.has_point(Point2(b->get_position().x, b->get_position().y))) { - edit_selected(); + if (!edit_selected()) { + emit_signal("item_double_clicked"); + } } else { emit_signal("item_double_clicked"); } diff --git a/scene/gui/tree.h b/scene/gui/tree.h index f12d8fc4d2..47befb0c15 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -201,6 +201,7 @@ public: void add_button(int p_column, const Ref<Texture> &p_button, int p_id = -1, bool p_disabled = false, const String &p_tooltip = ""); int get_button_count(int p_column) const; + String get_button_tooltip(int p_column, int p_idx) const; Ref<Texture> get_button(int p_column, int p_idx) const; int get_button_id(int p_column, int p_idx) const; void erase_button(int p_column, int p_idx); |