diff options
Diffstat (limited to 'scene/gui')
-rw-r--r-- | scene/gui/base_button.cpp | 7 | ||||
-rw-r--r-- | scene/gui/color_picker.cpp | 51 | ||||
-rw-r--r-- | scene/gui/color_picker.h | 3 | ||||
-rw-r--r-- | scene/gui/control.cpp | 50 | ||||
-rw-r--r-- | scene/gui/dialogs.h | 2 | ||||
-rw-r--r-- | scene/gui/file_dialog.cpp | 26 | ||||
-rw-r--r-- | scene/gui/file_dialog.h | 2 | ||||
-rw-r--r-- | scene/gui/gradient_edit.cpp | 7 | ||||
-rw-r--r-- | scene/gui/grid_container.cpp | 28 | ||||
-rw-r--r-- | scene/gui/item_list.cpp | 90 | ||||
-rw-r--r-- | scene/gui/item_list.h | 2 | ||||
-rw-r--r-- | scene/gui/line_edit.cpp | 2 | ||||
-rw-r--r-- | scene/gui/option_button.cpp | 10 | ||||
-rw-r--r-- | scene/gui/option_button.h | 1 | ||||
-rw-r--r-- | scene/gui/popup_menu.cpp | 116 | ||||
-rw-r--r-- | scene/gui/rich_text_label.cpp | 22 | ||||
-rw-r--r-- | scene/gui/rich_text_label.h | 2 | ||||
-rw-r--r-- | scene/gui/scroll_bar.cpp | 56 | ||||
-rw-r--r-- | scene/gui/slider.cpp | 26 | ||||
-rw-r--r-- | scene/gui/tab_container.cpp | 22 | ||||
-rw-r--r-- | scene/gui/tab_container.h | 1 | ||||
-rw-r--r-- | scene/gui/tabs.cpp | 2 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 21 | ||||
-rw-r--r-- | scene/gui/texture_progress.cpp | 57 | ||||
-rw-r--r-- | scene/gui/tree.cpp | 520 | ||||
-rw-r--r-- | scene/gui/tree.h | 4 |
26 files changed, 617 insertions, 513 deletions
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 9dfd388c3d..562dd155f9 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -211,6 +211,11 @@ void BaseButton::_gui_input(Ref<InputEvent> p_event) { if (!toggle_mode) { //mouse press attempt pressed(); + if (get_script_instance()) { + Variant::CallError ce; + get_script_instance()->call(SceneStringNames::get_singleton()->_pressed, NULL, 0, ce); + } + emit_signal("pressed"); } else { @@ -307,6 +312,8 @@ void BaseButton::toggled(bool p_pressed) { } void BaseButton::set_disabled(bool p_disabled) { + if (status.disabled == p_disabled) + return; status.disabled = p_disabled; update(); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 30bcc48149..31be18612f 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -77,8 +77,7 @@ void ColorPicker::_notification(int p_what) { void ColorPicker::set_focus_on_line_edit() { - c_text->grab_focus(); - c_text->select(); + c_text->call_deferred("grab_focus"); } void ColorPicker::_update_controls() { @@ -159,14 +158,16 @@ void ColorPicker::_update_color() { updating = true; for (int i = 0; i < 4; i++) { - scroll[i]->set_max(255); scroll[i]->set_step(0.01); if (raw_mode_enabled) { + scroll[i]->set_max(100); if (i == 3) scroll[i]->set_max(1); scroll[i]->set_value(color.components[i]); } else { - scroll[i]->set_value(color.components[i] * 255); + const int byte_value = color.components[i] * 255; + scroll[i]->set_max(next_power_of_2(MAX(255, byte_value)) - 1); + scroll[i]->set_value(byte_value); } } @@ -242,6 +243,7 @@ bool ColorPicker::is_raw_mode() const { } void ColorPicker::_update_text_value() { + bool visible = true; if (text_is_constructor) { String t = "Color(" + String::num(color.r) + "," + String::num(color.g) + "," + String::num(color.b); if (edit_alpha && color.a < 1) @@ -250,8 +252,13 @@ void ColorPicker::_update_text_value() { t += ")"; c_text->set_text(t); } else { - c_text->set_text(color.to_html(edit_alpha && color.a < 1)); + if (color.r > 1 || color.g > 1 || color.b > 1 || color.r < 0 || color.g < 0 || color.b < 0) { + visible = false; + } else { + c_text->set_text(color.to_html(edit_alpha && color.a < 1)); + } } + c_text->set_visible(visible); } void ColorPicker::_sample_draw() { @@ -462,6 +469,31 @@ void ColorPicker::_screen_pick_pressed() { screen->show_modal(); } +void ColorPicker::_focus_enter() { + if (c_text->has_focus()) { + c_text->select_all(); + return; + } + for (int i = 0; i < 4; i++) { + if (values[i]->get_line_edit()->has_focus()) { + values[i]->get_line_edit()->select_all(); + break; + } + } +} + +void ColorPicker::_focus_exit() { + for (int i = 0; i < 4; i++) { + values[i]->get_line_edit()->select(0, 0); + } + c_text->select(0, 0); +} + +void ColorPicker::_html_focus_exit() { + _html_entered(c_text->get_text()); + _focus_exit(); +} + void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPicker::set_pick_color); @@ -483,6 +515,9 @@ void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("_w_input"), &ColorPicker::_w_input); ClassDB::bind_method(D_METHOD("_preset_input"), &ColorPicker::_preset_input); ClassDB::bind_method(D_METHOD("_screen_input"), &ColorPicker::_screen_input); + ClassDB::bind_method(D_METHOD("_focus_enter"), &ColorPicker::_focus_enter); + ClassDB::bind_method(D_METHOD("_focus_exit"), &ColorPicker::_focus_exit); + ClassDB::bind_method(D_METHOD("_html_focus_exit"), &ColorPicker::_html_focus_exit); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); @@ -559,11 +594,14 @@ ColorPicker::ColorPicker() : scroll[i] = memnew(HSlider); scroll[i]->set_v_size_flags(SIZE_SHRINK_CENTER); + scroll[i]->set_focus_mode(FOCUS_NONE); hbc->add_child(scroll[i]); values[i] = memnew(SpinBox); scroll[i]->share(values[i]); hbc->add_child(values[i]); + values[i]->get_line_edit()->connect("focus_entered", this, "_focus_enter"); + values[i]->get_line_edit()->connect("focus_exited", this, "_focus_exit"); scroll[i]->set_min(0); scroll[i]->set_page(0); @@ -589,6 +627,9 @@ ColorPicker::ColorPicker() : c_text = memnew(LineEdit); hhb->add_child(c_text); c_text->connect("text_entered", this, "_html_entered"); + c_text->connect("focus_entered", this, "_focus_enter"); + c_text->connect("focus_exited", this, "_html_focus_exit"); + text_type->set_text("#"); c_text->set_h_size_flags(SIZE_EXPAND_FILL); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 01ae1cc464..40ded4fff5 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -88,6 +88,9 @@ private: void _screen_input(const Ref<InputEvent> &p_event); void _add_preset_pressed(); void _screen_pick_pressed(); + void _focus_enter(); + void _focus_exit(); + void _html_focus_exit(); protected: void _notification(int); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 01415594d3..a5883863cd 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -49,31 +49,41 @@ Dictionary Control::_edit_get_state() const { Dictionary s; - s["rect"] = get_rect(); s["rotation"] = get_rotation(); s["scale"] = get_scale(); + s["pivot"] = get_pivot_offset(); Array anchors; anchors.push_back(get_anchor(MARGIN_LEFT)); anchors.push_back(get_anchor(MARGIN_TOP)); anchors.push_back(get_anchor(MARGIN_RIGHT)); anchors.push_back(get_anchor(MARGIN_BOTTOM)); s["anchors"] = anchors; + Array margins; + margins.push_back(get_margin(MARGIN_LEFT)); + margins.push_back(get_margin(MARGIN_TOP)); + margins.push_back(get_margin(MARGIN_RIGHT)); + margins.push_back(get_margin(MARGIN_BOTTOM)); + s["margins"] = margins; return s; } void Control::_edit_set_state(const Dictionary &p_state) { Dictionary state = p_state; - Rect2 rect = state["rect"]; - set_position(rect.position); - set_size(rect.size); set_rotation(state["rotation"]); set_scale(state["scale"]); + set_pivot_offset(state["pivot"]); Array anchors = state["anchors"]; - set_anchor(MARGIN_LEFT, anchors[0]); - set_anchor(MARGIN_TOP, anchors[1]); - set_anchor(MARGIN_RIGHT, anchors[2]); - set_anchor(MARGIN_BOTTOM, anchors[3]); + data.anchor[MARGIN_LEFT] = anchors[0]; + data.anchor[MARGIN_TOP] = anchors[1]; + data.anchor[MARGIN_RIGHT] = anchors[2]; + data.anchor[MARGIN_BOTTOM] = anchors[3]; + Array margins = state["margins"]; + data.margin[MARGIN_LEFT] = margins[0]; + data.margin[MARGIN_TOP] = margins[1]; + data.margin[MARGIN_RIGHT] = margins[2]; + data.margin[MARGIN_BOTTOM] = margins[3]; + _size_changed(); } void Control::_edit_set_position(const Point2 &p_position) { @@ -85,19 +95,8 @@ Point2 Control::_edit_get_position() const { }; void Control::_edit_set_rect(const Rect2 &p_edit_rect) { - - Transform2D xform = _get_internal_transform(); - - Vector2 new_pos = xform.basis_xform(p_edit_rect.position); - - Vector2 pos = get_position() + new_pos; - - Rect2 new_rect = get_rect(); - new_rect.position = pos.snapped(Vector2(1, 1)); - new_rect.size = p_edit_rect.size.snapped(Vector2(1, 1)); - - set_position(new_rect.position); - set_size(new_rect.size); + set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1))); + set_size(p_edit_rect.size.snapped(Vector2(1, 1))); } Rect2 Control::_edit_get_rect() const { @@ -121,6 +120,9 @@ bool Control::_edit_use_rotation() const { } void Control::_edit_set_pivot(const Point2 &p_pivot) { + Vector2 delta_pivot = p_pivot - get_pivot_offset(); + Vector2 move = Vector2((cos(data.rotation) - 1.0) * delta_pivot.x - sin(data.rotation) * delta_pivot.y, sin(data.rotation) * delta_pivot.x + (cos(data.rotation) - 1.0) * delta_pivot.y); + set_position(get_position() + move); set_pivot_offset(p_pivot); } @@ -1297,7 +1299,8 @@ void Control::_size_changed() { new_size_cache.height = MAX(minimum_size.height, new_size_cache.height); } - if (get_viewport()->is_snap_controls_to_pixels_enabled()) { + // We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot() + if (Math::abs(Math::sin(data.rotation * 4.0f)) < 0.00001f && get_viewport()->is_snap_controls_to_pixels_enabled()) { new_size_cache = new_size_cache.floor(); new_pos_cache = new_pos_cache.floor(); } @@ -1378,7 +1381,6 @@ void Control::set_anchor(Margin p_margin, float p_anchor, bool p_keep_margin, bo data.margin[(p_margin + 2) % 4] = _s2a(previous_opposite_margin_pos, data.anchor[(p_margin + 2) % 4], parent_range); } } - if (is_inside_tree()) { _size_changed(); } @@ -2847,7 +2849,7 @@ void Control::_bind_methods() { ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "rect_rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.01"), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale"); ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset"); - ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents"); ADD_GROUP("Hint", "hint_"); ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip"); diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index e61ede7c3d..feb080dd06 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -108,7 +108,7 @@ class AcceptDialog : public WindowDialog { HBoxContainer *hbc; Label *label; Button *ok; - //Button *cancel; no more cancel (there is X on tht titlebar) + //Button *cancel; no more cancel (there is X on that titlebar) bool hide_on_ok; void _custom_action(const String &p_action); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 58717edbae..4bd92d888d 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -210,7 +210,7 @@ void FileDialog::_action_pressed() { bool valid = false; if (filter->get_selected() == filter->get_item_count() - 1) { - valid = true; //match none + valid = true; // match none } else if (filters.size() > 1 && filter->get_selected() == 0) { // match all filters for (int i = 0; i < filters.size(); i++) { @@ -287,7 +287,7 @@ bool FileDialog::_is_open_should_be_disabled() { TreeItem *ti = tree->get_selected(); // We have something that we can't select? if (!ti) - return true; + return mode != MODE_OPEN_DIR; // In "Open folder" mode, having nothing selected picks the current folder. Dictionary d = ti->get_metadata(0); @@ -319,17 +319,15 @@ void FileDialog::deselect_items() { case MODE_OPEN_FILE: case MODE_OPEN_FILES: - get_ok()->set_text(TTR("Open")); - get_ok()->set_disabled(false); + get_ok()->set_text(RTR("Open")); break; - case MODE_OPEN_DIR: - get_ok()->set_text(TTR("Select Current Folder")); - get_ok()->set_disabled(false); + get_ok()->set_text(RTR("Select Current Folder")); break; } } } + void FileDialog::_tree_selected() { TreeItem *ti = tree->get_selected(); @@ -341,13 +339,13 @@ void FileDialog::_tree_selected() { file->set_text(d["name"]); } else if (mode == MODE_OPEN_DIR) { - get_ok()->set_text(TTR("Select this Folder")); + get_ok()->set_text(RTR("Select this Folder")); } get_ok()->set_disabled(_is_open_should_be_disabled()); } -void FileDialog::_tree_dc_selected() { +void FileDialog::_tree_item_activated() { TreeItem *ti = tree->get_selected(); if (!ti) @@ -756,7 +754,7 @@ void FileDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("_unhandled_input"), &FileDialog::_unhandled_input); ClassDB::bind_method(D_METHOD("_tree_selected"), &FileDialog::_tree_selected); - ClassDB::bind_method(D_METHOD("_tree_db_selected"), &FileDialog::_tree_dc_selected); + ClassDB::bind_method(D_METHOD("_tree_item_activated"), &FileDialog::_tree_item_activated); ClassDB::bind_method(D_METHOD("_dir_entered"), &FileDialog::_dir_entered); ClassDB::bind_method(D_METHOD("_file_entered"), &FileDialog::_file_entered); ClassDB::bind_method(D_METHOD("_action_pressed"), &FileDialog::_action_pressed); @@ -845,7 +843,7 @@ FileDialog::FileDialog() { HBoxContainer *hbc = memnew(HBoxContainer); dir_up = memnew(ToolButton); - dir_up->set_tooltip(TTR("Go to parent folder")); + dir_up->set_tooltip(RTR("Go to parent folder")); hbc->add_child(dir_up); dir_up->connect("pressed", this, "_go_up"); @@ -881,7 +879,7 @@ FileDialog::FileDialog() { filter = memnew(OptionButton); filter->set_stretch_ratio(3); filter->set_h_size_flags(SIZE_EXPAND_FILL); - filter->set_clip_text(true); //too many extensions overflow it + filter->set_clip_text(true); // too many extensions overflows it hbc->add_child(filter); vbc->add_child(hbc); @@ -890,9 +888,8 @@ FileDialog::FileDialog() { _update_drives(); connect("confirmed", this, "_action_pressed"); - //cancel->connect("pressed", this,"_cancel_pressed"); tree->connect("cell_selected", this, "_tree_selected", varray(), CONNECT_DEFERRED); - tree->connect("item_activated", this, "_tree_db_selected", varray()); + tree->connect("item_activated", this, "_tree_item_activated", varray()); tree->connect("nothing_selected", this, "deselect_items"); dir->connect("text_entered", this, "_dir_entered"); file->connect("text_entered", this, "_file_entered"); @@ -922,7 +919,6 @@ FileDialog::FileDialog() { exterr->set_text(RTR("Must use a valid extension.")); add_child(exterr); - //update_file_list(); update_filters(); update_dir(); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 2a09494682..ad483d5dab 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -107,7 +107,7 @@ private: void _tree_selected(); void _select_drive(int p_idx); - void _tree_dc_selected(); + void _tree_item_activated(); void _dir_entered(String p_dir); void _file_entered(const String &p_file); void _action_pressed(); diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index 3985039716..9fc8e98a7f 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -367,6 +367,13 @@ void GradientEdit::_notification(int p_what) { draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); } } + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + + if (!is_visible()) { + grabbing = false; + } + } } void GradientEdit::_draw_checker(int x, int y, int w, int h) { diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index c2b8a7dfab..b401abd436 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -36,10 +36,10 @@ void GridContainer::_notification(int p_what) { case NOTIFICATION_SORT_CHILDREN: { - Map<int, int> col_minw; - Map<int, int> row_minh; - Set<int> col_expanded; - Set<int> row_expanded; + 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"); @@ -84,17 +84,17 @@ void GridContainer::_notification(int p_what) { if (!row_expanded.has(E->key())) remaining_space.height -= E->get(); } - remaining_space.height -= vsep * (max_row - 1); - remaining_space.width -= hsep * (max_col - 1); + remaining_space.height -= vsep * MAX(max_row - 1, 0); + remaining_space.width -= hsep * MAX(max_col - 1, 0); bool can_fit = false; - while (!can_fit) { + while (!can_fit && col_expanded.size() > 0) { // Check if all minwidth constraints are ok if we use the remaining space can_fit = true; - int max_index = 0; + int max_index = col_expanded.front()->get(); for (Set<int>::Element *E = col_expanded.front(); E; E = E->next()) { if (col_minw[E->get()] > col_minw[max_index]) { - max_index = col_minw[E->get()]; + max_index = E->get(); } if (can_fit && (remaining_space.width / col_expanded.size()) < col_minw[E->get()]) { can_fit = false; @@ -109,13 +109,13 @@ void GridContainer::_notification(int p_what) { } can_fit = false; - while (!can_fit) { + while (!can_fit && row_expanded.size() > 0) { // Check if all minwidth constraints are ok if we use the remaining space can_fit = true; - int max_index = 0; + int max_index = row_expanded.front()->get(); for (Set<int>::Element *E = row_expanded.front(); E; E = E->next()) { if (row_minh[E->get()] > row_minh[max_index]) { - max_index = row_minh[E->get()]; + max_index = E->get(); } if (can_fit && (remaining_space.height / row_expanded.size()) < row_minh[E->get()]) { can_fit = false; @@ -130,8 +130,8 @@ void GridContainer::_notification(int p_what) { } // Finally, fit the nodes - int col_expand = remaining_space.width / col_expanded.size(); - int row_expand = remaining_space.height / row_expanded.size(); + 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; int col_ofs = 0; int row_ofs = 0; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index fe85d04003..cc17e6bcd8 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -295,35 +295,21 @@ int ItemList::get_current() const { return current; } -void ItemList::move_item(int p_item, int p_to_pos) { +void ItemList::move_item(int p_from_idx, int p_to_idx) { - ERR_FAIL_INDEX(p_item, items.size()); - ERR_FAIL_INDEX(p_to_pos, items.size() + 1); + ERR_FAIL_INDEX(p_from_idx, items.size()); + ERR_FAIL_INDEX(p_to_idx, items.size()); - Item it = items[p_item]; - items.remove(p_item); - - if (p_to_pos > p_item) { - p_to_pos--; - } - - if (p_to_pos >= items.size()) { - items.push_back(it); - } else { - items.insert(p_to_pos, it); + if (is_anything_selected() && get_selected_items()[0] == p_from_idx) { + current = p_to_idx; } - if (current < 0) { - //do none - } else if (p_item == current) { - current = p_to_pos; - } else if (p_to_pos > p_item && current > p_item && current < p_to_pos) { - current--; - } else if (p_to_pos < p_item && current < p_item && current > p_to_pos) { - current++; - } + Item item = items[p_from_idx]; + items.remove(p_from_idx); + items.insert(p_to_idx, item); update(); + shape_changed = true; } int ItemList::get_item_count() const { @@ -961,12 +947,36 @@ void ItemList::_notification(int p_what) { Vector2 base_ofs = bg->get_offset(); base_ofs.y -= int(scroll_bar->get_value()); - Rect2 clip(Point2(), size - bg->get_minimum_size() + Vector2(0, scroll_bar->get_value())); + const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there + + int first_item_visible; + { + // do a binary search to find the first item whose rect reaches below clip.position.y + int lo = 0; + int hi = items.size(); + while (lo < hi) { + const int mid = (lo + hi) / 2; + const Rect2 &rcache = items[mid].rect_cache; + if (rcache.position.y + rcache.size.y < clip.position.y) { + lo = mid + 1; + } else { + hi = mid; + } + } + // we might have ended up with column 2, or 3, ..., so let's find the first column + while (lo > 0 && items[lo - 1].rect_cache.position.y == items[lo].rect_cache.position.y) { + lo -= 1; + } + first_item_visible = lo; + } - for (int i = 0; i < items.size(); i++) { + for (int i = first_item_visible; i < items.size(); i++) { Rect2 rcache = items[i].rect_cache; + if (rcache.position.y > clip.position.y + clip.size.y) + break; // done + if (!clip.intersects(rcache)) continue; @@ -1138,8 +1148,28 @@ void ItemList::_notification(int p_what) { } } - for (int i = 0; i < separators.size(); i++) { - draw_line(Vector2(bg->get_margin(MARGIN_LEFT), base_ofs.y + separators[i]), Vector2(width, base_ofs.y + separators[i]), guide_color); + int first_visible_separator = 0; + { + // do a binary search to find the first separator that is below clip_position.y + int lo = 0; + int hi = separators.size(); + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (separators[mid] < clip.position.y) { + lo = mid + 1; + } else { + hi = mid; + } + } + first_visible_separator = lo; + } + + for (int i = first_visible_separator; i < separators.size(); i++) { + if (separators[i] > clip.position.y + clip.size.y) + break; // done + + const int y = base_ofs.y + separators[i]; + draw_line(Vector2(bg->get_margin(MARGIN_LEFT), y), Vector2(width, y), guide_color); } } } @@ -1379,9 +1409,13 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("select", "idx", "single"), &ItemList::select, DEFVAL(true)); ClassDB::bind_method(D_METHOD("unselect", "idx"), &ItemList::unselect); + ClassDB::bind_method(D_METHOD("unselect_all"), &ItemList::unselect_all); + ClassDB::bind_method(D_METHOD("is_selected", "idx"), &ItemList::is_selected); ClassDB::bind_method(D_METHOD("get_selected_items"), &ItemList::get_selected_items); + ClassDB::bind_method(D_METHOD("move_item", "p_from_idx", "p_to_idx"), &ItemList::move_item); + ClassDB::bind_method(D_METHOD("get_item_count"), &ItemList::get_item_count); ClassDB::bind_method(D_METHOD("remove_item", "idx"), &ItemList::remove_item); @@ -1421,6 +1455,8 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_height", "enable"), &ItemList::set_auto_height); ClassDB::bind_method(D_METHOD("has_auto_height"), &ItemList::has_auto_height); + ClassDB::bind_method(D_METHOD("is_anything_selected"), &ItemList::is_anything_selected); + ClassDB::bind_method(D_METHOD("get_item_at_position", "position", "exact"), &ItemList::get_item_at_position, DEFVAL(false)); ClassDB::bind_method(D_METHOD("ensure_current_is_visible"), &ItemList::ensure_current_is_visible); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 7f34a250bd..0fa0dd415b 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -169,7 +169,7 @@ public: void set_current(int p_current); int get_current() const; - void move_item(int p_item, int p_to_pos); + void move_item(int p_from_idx, int p_to_idx); int get_item_count() const; void remove_item(int p_idx); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 03dc6686b8..5c0e8fefc7 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -373,12 +373,14 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) { case KEY_UP: { shift_selection_check_pre(k->get_shift()); + if (get_cursor_position() == 0) handled = false; set_cursor_position(0); shift_selection_check_post(k->get_shift()); } break; case KEY_DOWN: { shift_selection_check_pre(k->get_shift()); + if (get_cursor_position() == text.length()) handled = false; set_cursor_position(text.length()); shift_selection_check_post(k->get_shift()); } break; diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 1a46921561..6e53f11b99 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -75,6 +75,10 @@ void OptionButton::_notification(int p_what) { } } +void OptionButton::_focused(int p_which) { + emit_signal("item_focused", p_which); +} + void OptionButton::_selected(int p_which) { int selid = -1; @@ -290,6 +294,7 @@ void OptionButton::get_translatable_strings(List<String> *p_strings) const { void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_selected"), &OptionButton::_selected); + ClassDB::bind_method(D_METHOD("_focused"), &OptionButton::_focused); ClassDB::bind_method(D_METHOD("add_item", "label", "id"), &OptionButton::add_item, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("add_icon_item", "texture", "label", "id"), &OptionButton::add_icon_item); @@ -318,9 +323,11 @@ void OptionButton::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_items"), &OptionButton::_set_items); ClassDB::bind_method(D_METHOD("_get_items"), &OptionButton::_get_items); - ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items"); + // "selected" property must come after "items", otherwise GH-10213 occurs + ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected"); ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "ID"))); + ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "ID"))); } OptionButton::OptionButton() { @@ -335,6 +342,7 @@ OptionButton::OptionButton() { popup->set_as_toplevel(true); popup->set_pass_on_modal_close_click(false); popup->connect("id_pressed", this, "_selected"); + popup->connect("id_focused", this, "_focused"); } OptionButton::~OptionButton() { diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index f65fa1b631..d5f866d806 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -43,6 +43,7 @@ class OptionButton : public Button { PopupMenu *popup; int current; + void _focused(int p_which); void _selected(int p_which); void _select(int p_which, bool p_emit = false); void _select_int(int p_which); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 89000fcde1..747230e69f 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -211,86 +211,69 @@ void PopupMenu::_scroll(float p_factor, const Point2 &p_over) { void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) { - Ref<InputEventKey> k = p_event; - - if (k.is_valid()) { - - if (!k->is_pressed()) - return; - - switch (k->get_scancode()) { - - case KEY_DOWN: { - - int search_from = mouse_over + 1; - if (search_from >= items.size()) - search_from = 0; - - for (int i = search_from; i < items.size(); i++) { + if (p_event->is_action("ui_down") && p_event->is_pressed()) { - if (i < 0 || i >= items.size()) - continue; + int search_from = mouse_over + 1; + if (search_from >= items.size()) + search_from = 0; - if (!items[i].separator && !items[i].disabled) { + for (int i = search_from; i < items.size(); i++) { - mouse_over = i; - update(); - break; - } - } - } break; - case KEY_UP: { - - int search_from = mouse_over - 1; - if (search_from < 0) - search_from = items.size() - 1; - - for (int i = search_from; i >= 0; i--) { - - if (i < 0 || i >= items.size()) - continue; - - if (!items[i].separator && !items[i].disabled) { + if (i < 0 || i >= items.size()) + continue; - mouse_over = i; - update(); - break; - } - } - } break; + if (!items[i].separator && !items[i].disabled) { - case KEY_LEFT: { + mouse_over = i; + emit_signal("id_focused", i); + update(); + accept_event(); + break; + } + } + } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { - Node *n = get_parent(); - if (!n) - break; + int search_from = mouse_over - 1; + if (search_from < 0) + search_from = items.size() - 1; - PopupMenu *pm = Object::cast_to<PopupMenu>(n); - if (!pm) - break; + for (int i = search_from; i >= 0; i--) { - hide(); - } break; + if (i < 0 || i >= items.size()) + continue; - case KEY_RIGHT: { + if (!items[i].separator && !items[i].disabled) { - if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && items[mouse_over].submenu != "" && submenu_over != mouse_over) - _activate_submenu(mouse_over); - } break; + mouse_over = i; + emit_signal("id_focused", i); + update(); + accept_event(); + break; + } + } + } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { - case KEY_ENTER: - case KEY_KP_ENTER: { + Node *n = get_parent(); + if (n && Object::cast_to<PopupMenu>(n)) { + hide(); + accept_event(); + } + } else if (p_event->is_action("ui_right") && p_event->is_pressed()) { - if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && items[mouse_over].submenu != "" && submenu_over != mouse_over) { + _activate_submenu(mouse_over); + accept_event(); + } + } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { - if (items[mouse_over].submenu != "" && submenu_over != mouse_over) { - _activate_submenu(mouse_over); - break; - } + if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) { - activate_item(mouse_over); - } - } break; + if (items[mouse_over].submenu != "" && submenu_over != mouse_over) { + _activate_submenu(mouse_over); + } else { + activate_item(mouse_over); + } + accept_event(); } } @@ -1229,6 +1212,7 @@ void PopupMenu::_bind_methods() { ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "hide_on_state_item_selection"), "set_hide_on_state_item_selection", "is_hide_on_state_item_selection"); ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "ID"))); + ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "ID"))); ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index"))); } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 381c6c75a5..ae07d5e671 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -125,6 +125,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & l.descent_caches.clear(); l.char_count = 0; l.minimum_width = 0; + l.maximum_width = 0; } int wofs = margin; @@ -200,7 +201,8 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & #define ENSURE_WIDTH(m_width) \ if (p_mode == PROCESS_CACHE) { \ - l.minimum_width = MAX(l.minimum_width, wofs + m_width); \ + l.maximum_width = MAX(l.maximum_width, MIN(p_width, wofs + m_width)); \ + l.minimum_width = MAX(l.minimum_width, m_width); \ } \ if (wofs + m_width > p_width) { \ if (p_mode == PROCESS_CACHE) { \ @@ -247,6 +249,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & int rchar = 0; int lh = 0; bool line_is_blank = true; + int fh = 0; while (it) { @@ -262,14 +265,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & const CharType *c = text->text.c_str(); const CharType *cf = c; - int fh = font->get_height(); int ascent = font->get_ascent(); int descent = font->get_descent(); - line_ascent = MAX(line_ascent, ascent); - line_descent = MAX(line_descent, descent); - fh = MAX(fh, line_ascent + line_descent); // various fonts! - Color color; bool underline = false; @@ -317,8 +315,13 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & end++; } + CHECK_HEIGHT(fh); ENSURE_WIDTH(w); + line_ascent = MAX(line_ascent, ascent); + line_descent = MAX(line_descent, descent); + fh = line_ascent + line_descent; + if (end && c[end - 1] == ' ') { if (p_mode == PROCESS_CACHE) { spaces_size += font->get_char_size(' ').width; @@ -468,6 +471,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & //set minimums to zero for (int i = 0; i < table->columns.size(); i++) { table->columns[i].min_width = 0; + table->columns[i].max_width = 0; table->columns[i].width = 0; } //compute minimum width for each cell @@ -485,6 +489,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color()); table->columns[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width); + table->columns[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width); } idx++; } @@ -497,12 +502,13 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & for (int i = 0; i < table->columns.size(); i++) { remaining_width -= table->columns[i].min_width; + if (table->columns[i].max_width > table->columns[i].min_width) + table->columns[i].expand = true; if (table->columns[i].expand) total_ratio += table->columns[i].expand_ratio; } //assign actual widths - for (int i = 0; i < table->columns.size(); i++) { table->columns[i].width = table->columns[i].min_width; if (table->columns[i].expand) @@ -1632,7 +1638,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { tag_stack.push_front(tag); } else if (tag.begins_with("cell=")) { - int ratio = tag.substr(6, tag.length()).to_int(); + int ratio = tag.substr(5, tag.length()).to_int(); if (ratio < 1) ratio = 1; //use monospace font diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index e7d5e6bb1b..83938cff61 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -87,6 +87,7 @@ private: int height_accum_cache; int char_count; int minimum_width; + int maximum_width; Line() { from = NULL; @@ -199,6 +200,7 @@ private: bool expand; int expand_ratio; int min_width; + int max_width; int width; }; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 95fcda2db3..e1cabd3f88 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -199,54 +199,40 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) { } } - Ref<InputEventKey> k = p_event; + if (p_event->is_pressed()) { - if (k.is_valid()) { + if (p_event->is_action("ui_left")) { - if (!k->is_pressed()) - return; - - switch (k->get_scancode()) { - - case KEY_LEFT: { - - if (orientation != HORIZONTAL) - return; - set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); + if (orientation != HORIZONTAL) + return; + set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); - } break; - case KEY_RIGHT: { + } else if (p_event->is_action("ui_right")) { - if (orientation != HORIZONTAL) - return; - set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); - - } break; - case KEY_UP: { + if (orientation != HORIZONTAL) + return; + set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); - if (orientation != VERTICAL) - return; + } else if (p_event->is_action("ui_up")) { - set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); + if (orientation != VERTICAL) + return; - } break; - case KEY_DOWN: { + set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); - if (orientation != VERTICAL) - return; - set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); + } else if (p_event->is_action("ui_down")) { - } break; - case KEY_HOME: { + if (orientation != VERTICAL) + return; + set_value(get_value() + (custom_step >= 0 ? custom_step : get_step())); - set_value(get_min()); + } else if (p_event->is_action("ui_home")) { - } break; - case KEY_END: { + set_value(get_min()); - set_value(get_max()); + } else if (p_event->is_action("ui_end")) { - } break; + set_value(get_max()); } } } diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index a7a1b499c3..46215c9277 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -118,28 +118,14 @@ void Slider::_gui_input(Ref<InputEvent> p_event) { return; set_value(get_value() - (custom_step >= 0 ? custom_step : get_step())); accept_event(); + } else if (p_event->is_action("ui_home") && p_event->is_pressed()) { - } else { - - Ref<InputEventKey> k = p_event; - - if (!k.is_valid() || !k->is_pressed()) - return; - - switch (k->get_scancode()) { - - case KEY_HOME: { - - set_value(get_min()); - accept_event(); - } break; - case KEY_END: { - - set_value(get_max()); - accept_event(); + set_value(get_min()); + accept_event(); + } else if (p_event->is_action("ui_end") && p_event->is_pressed()) { - } break; - } + set_value(get_max()); + accept_event(); } } } diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 0312e58094..6e85ce5eb4 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -474,21 +474,24 @@ void TabContainer::remove_child_notify(Node *p_child) { Control::remove_child_notify(p_child); - int tc = get_tab_count(); - if (current == tc - 1) { - current--; - if (current < 0) - current = 0; - else { - call_deferred("set_current_tab", current); - } - } + call_deferred("_update_current_tab"); p_child->disconnect("renamed", this, "_child_renamed_callback"); update(); } +void TabContainer::_update_current_tab() { + + int tc = get_tab_count(); + if (current >= tc) + current = tc - 1; + if (current < 0) + current = 0; + else + set_current_tab(current); +} + void TabContainer::set_tab_align(TabAlign p_align) { ERR_FAIL_INDEX(p_align, 3); @@ -664,6 +667,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("_update_current_tab"), &TabContainer::_update_current_tab); ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab"))); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 0ba8c205ea..4bc6e00145 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -62,6 +62,7 @@ private: Vector<Control *> _get_tabs() const; int _get_tab_width(int p_index) const; void _on_theme_changed(); + void _update_current_tab(); protected: void _child_renamed_callback(); diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index f0e89877cd..dee32aef7a 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -882,4 +882,6 @@ Tabs::Tabs() { min_width = 0; scrolling_enabled = true; + buttons_visible = false; + hover = -1; } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index e8454e021f..48bd733e80 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -628,7 +628,7 @@ void TextEdit::_notification(int p_what) { VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color); } //compute actual region to start (may be inside say, a comment). - //slow in very large documments :( but ok for source! + //slow in very large documents :( but ok for source! for (int i = 0; i < cursor.line_ofs; i++) { @@ -776,7 +776,6 @@ void TextEdit::_notification(int p_what) { j--; } if (escaped) { - j--; cc = '\\'; continue; } @@ -2142,9 +2141,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (completion_index > 0) { completion_index--; - completion_current = completion_options[completion_index]; - update(); + } else { + completion_index = completion_options.size() - 1; } + completion_current = completion_options[completion_index]; + update(); + accept_event(); return; } @@ -2153,9 +2155,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) { if (completion_index < completion_options.size() - 1) { completion_index++; - completion_current = completion_options[completion_index]; - update(); + } else { + completion_index = 0; } + completion_current = completion_options[completion_index]; + update(); + accept_event(); return; } @@ -4471,7 +4476,7 @@ bool TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_from_l ERR_FAIL_INDEX_V(p_from_line, text.size(), false); ERR_FAIL_INDEX_V(p_from_column, text[p_from_line].length() + 1, false); - //search through the whole documment, but start by current line + //search through the whole document, but start by current line int line = p_from_line; int pos = -1; @@ -5233,7 +5238,7 @@ void TextEdit::_update_completion_candidates() { } else { - while (cofs > 0 && l[cofs - 1] > 32 && _is_completable(l[cofs - 1])) { + while (cofs > 0 && l[cofs - 1] > 32 && (l[cofs - 1] == '/' || _is_completable(l[cofs - 1]))) { s = String::chr(l[cofs - 1]) + s; if (l[cofs - 1] == '\'' || l[cofs - 1] == '"' || l[cofs - 1] == '$') break; diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 01b00c34ea..4b3ba6df3c 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -117,22 +117,45 @@ Point2 TextureProgress::unit_val_to_uv(float val) { Point2 p = get_relative_center(); - if (val < 0.125) - return Point2(p.x + (1 - p.x) * val * 8, 0); - if (val < 0.25) - return Point2(1, p.y * (val - 0.125) * 8); - if (val < 0.375) - return Point2(1, p.y + (1 - p.y) * (val - 0.25) * 8); - if (val < 0.5) - return Point2(1 - (1 - p.x) * (val - 0.375) * 8, 1); - if (val < 0.625) - return Point2(p.x * (1 - (val - 0.5) * 8), 1); - if (val < 0.75) - return Point2(0, 1 - ((1 - p.y) * (val - 0.625) * 8)); - if (val < 0.875) - return Point2(0, p.y - p.y * (val - 0.75) * 8); - else - return Point2(p.x * (val - 0.875) * 8, 0); + // Minimal version of Liang-Barsky clipping algorithm + float angle = (val * Math_TAU) - Math_PI * 0.5; + Point2 dir = Vector2(Math::cos(angle), Math::sin(angle)); + float t1 = 1.0; + float cp; + float cq; + float cr; + float edgeLeft = 0.0; + float edgeRight = 1.0; + float edgeBottom = 0.0; + float edgeTop = 1.0; + + for (int edge = 0; edge < 4; edge++) { + if (edge == 0) { + if (dir.x > 0) + continue; + cp = -dir.x; + cq = -(edgeLeft - p.x); + } else if (edge == 1) { + if (dir.x < 0) + continue; + cp = dir.x; + cq = (edgeRight - p.x); + } else if (edge == 2) { + if (dir.y > 0) + continue; + cp = -dir.y; + cq = -(edgeBottom - p.y); + } else if (edge == 3) { + if (dir.y < 0) + continue; + cp = dir.y; + cq = (edgeTop - p.y); + } + cr = cq / cp; + if (cr >= 0 && cr < t1) + t1 = cr; + } + return (p + t1 * dir); } Point2 TextureProgress::get_relative_center() { @@ -158,7 +181,7 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F if (p_ratio < 1.0) { // Drawing a partially-filled 9-patch is a little tricky - // texture is divided by 3 sections toward fill direction, - // then middle section is streching while the other two aren't. + // then middle section is stretching while the other two aren't. double width_total = 0.0; double width_texture = 0.0; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index cdbdc9b0d7..e7f63997f2 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2061,322 +2061,318 @@ void Tree::popup_select(int p_option) { item_edited(popup_edited_item_col, popup_edited_item); } -void Tree::_gui_input(Ref<InputEvent> p_event) { - - Ref<InputEventKey> k = p_event; +void Tree::_go_left() { + if (selected_col == 0) { + if (selected_item->get_children() != NULL && !selected_item->is_collapsed()) { + selected_item->set_collapsed(true); + } else { + if (columns.size() == 1) { // goto parent with one column + TreeItem *parent = selected_item->get_parent(); + if (selected_item != get_root() && parent && parent->is_selectable(selected_col) && !(hide_root && parent == get_root())) { + select_single_item(parent, get_root(), selected_col); + } + } else if (selected_item->get_prev_visible()) { + selected_col = columns.size() - 1; + _go_up(); // go to upper column if possible + } + } + } else { + if (select_mode == SELECT_MULTI) { + selected_col--; + emit_signal("cell_selected"); + } else { - if (k.is_valid()) { + selected_item->select(selected_col - 1); + } + } + update(); + accept_event(); + ensure_cursor_is_visible(); +} - if (!k->is_pressed()) - return; - if (k->get_command() || (k->get_shift() && k->get_unicode() == 0) || k->get_metakey()) - return; - if (!root) +void Tree::_go_right() { + if (selected_col == (columns.size() - 1)) { + if (selected_item->get_children() != NULL && selected_item->is_collapsed()) { + selected_item->set_collapsed(false); + } else if (selected_item->get_next_visible()) { + selected_item->select(0); + _go_down(); return; + } + } else { + if (select_mode == SELECT_MULTI) { + selected_col++; + emit_signal("cell_selected"); + } else { - if (hide_root && !root->get_next_visible()) + selected_item->select(selected_col + 1); + } + } + update(); + ensure_cursor_is_visible(); + accept_event(); +} + +void Tree::_go_up() { + TreeItem *prev = NULL; + if (!selected_item) { + prev = get_last_item(); + selected_col = 0; + } else { + + prev = selected_item->get_prev_visible(); + if (last_keypress != 0) { + //incr search next + int col; + prev = _search_item_text(prev, incr_search, &col, true, true); + if (!prev) { + accept_event(); + return; + } + } + } + + if (select_mode == SELECT_MULTI) { + + if (!prev) return; + selected_item = prev; + emit_signal("cell_selected"); + update(); + } else { - switch (k->get_scancode()) { -#define EXIT_BREAK \ - { \ - if (!cursor_can_exit_tree) accept_event(); \ - break; \ + int col = selected_col < 0 ? 0 : selected_col; + while (prev && !prev->cells[col].selectable) + prev = prev->get_prev_visible(); + if (!prev) + return; // do nothing.. + prev->select(col); } - case KEY_RIGHT: { - bool dobreak = true; - //TreeItem *next = NULL; - if (!selected_item) - break; - if (select_mode == SELECT_ROW) { - EXIT_BREAK; - } - if (selected_col > (columns.size() - 1)) { - EXIT_BREAK; - } - if (k->get_alt()) { - selected_item->set_collapsed(false); - TreeItem *next = selected_item->get_children(); - while (next && next != selected_item->next) { - next->set_collapsed(false); - next = next->get_next_visible(); - } - } else if (selected_col == (columns.size() - 1)) { - if (selected_item->get_children() != NULL && selected_item->is_collapsed()) { - selected_item->set_collapsed(false); - } else { - selected_col = 0; - dobreak = false; // fall through to key_down - } - } else { - if (select_mode == SELECT_MULTI) { - selected_col++; - emit_signal("cell_selected"); - } else { + ensure_cursor_is_visible(); + accept_event(); +} - selected_item->select(selected_col + 1); - } - } - update(); - ensure_cursor_is_visible(); +void Tree::_go_down() { + TreeItem *next = NULL; + if (!selected_item) { + + next = hide_root ? root->get_next_visible() : root; + selected_item = 0; + } else { + + next = selected_item->get_next_visible(); + + if (last_keypress != 0) { + //incr search next + int col; + next = _search_item_text(next, incr_search, &col, true); + if (!next) { accept_event(); - if (dobreak) { - break; - } + return; } - case KEY_DOWN: { + } + } - TreeItem *next = NULL; - if (!selected_item) { + if (select_mode == SELECT_MULTI) { - next = hide_root ? root->get_next_visible() : root; - selected_item = 0; - } else { + if (!next) { + return; + } - next = selected_item->get_next_visible(); + selected_item = next; + emit_signal("cell_selected"); + update(); + } else { - //if (diff < uint64_t(GLOBAL_DEF("gui/incr_search_max_interval_msec",2000))) { - if (last_keypress != 0) { - //incr search next - int col; - next = _search_item_text(next, incr_search, &col, true); - if (!next) { - accept_event(); - return; - } - } - } + int col = selected_col < 0 ? 0 : selected_col; - if (select_mode == SELECT_MULTI) { + while (next && !next->cells[col].selectable) + next = next->get_next_visible(); + if (!next) { + return; // do nothing.. + } + next->select(col); + } - if (!next) - EXIT_BREAK; + ensure_cursor_is_visible(); + accept_event(); +} - selected_item = next; - emit_signal("cell_selected"); - update(); - } else { +void Tree::_gui_input(Ref<InputEvent> p_event) { - int col = selected_col < 0 ? 0 : selected_col; + Ref<InputEventKey> k = p_event; - while (next && !next->cells[col].selectable) - next = next->get_next_visible(); - if (!next) - EXIT_BREAK; // do nothing.. - next->select(col); - } + if (p_event->is_action("ui_right") && p_event->is_pressed()) { - ensure_cursor_is_visible(); - accept_event(); + if (!cursor_can_exit_tree) accept_event(); - } break; - case KEY_LEFT: { - bool dobreak = true; + if (!selected_item || select_mode == SELECT_ROW || selected_col > (columns.size() - 1)) { + return; + } + if (k.is_valid() && k->get_alt()) { + selected_item->set_collapsed(false); + TreeItem *next = selected_item->get_children(); + while (next && next != selected_item->next) { + next->set_collapsed(false); + next = next->get_next_visible(); + } + } else { + _go_right(); + } + } else if (p_event->is_action("ui_left") && p_event->is_pressed()) { - //TreeItem *next = NULL; - if (!selected_item) - break; - if (select_mode == SELECT_ROW) { - EXIT_BREAK; - } - if (selected_col < 0) { - EXIT_BREAK; - } - if (k->get_alt()) { - selected_item->set_collapsed(true); - TreeItem *next = selected_item->get_children(); - while (next && next != selected_item->next) { - next->set_collapsed(true); - next = next->get_next_visible(); - } - } else if (selected_col == 0) { - if (selected_item->get_children() != NULL && !selected_item->is_collapsed()) { - selected_item->set_collapsed(true); - } else { - if (columns.size() == 1) { // goto parent with one column - TreeItem *parent = selected_item->get_parent(); - if (selected_item != get_root() && parent && parent->is_selectable(selected_col) && !(hide_root && parent == get_root())) { - select_single_item(parent, get_root(), selected_col); - } - } else { - selected_col = columns.size() - 1; - dobreak = false; // fall through to key_up - } - } - } else { - if (select_mode == SELECT_MULTI) { - selected_col--; - emit_signal("cell_selected"); - } else { + if (!cursor_can_exit_tree) accept_event(); - selected_item->select(selected_col - 1); - } - } - update(); - accept_event(); - ensure_cursor_is_visible(); + if (!selected_item || select_mode == SELECT_ROW || selected_col < 0) { + return; + } - if (dobreak) { - break; - } + if (k.is_valid() && k->get_alt()) { + selected_item->set_collapsed(true); + TreeItem *next = selected_item->get_children(); + while (next && next != selected_item->next) { + next->set_collapsed(true); + next = next->get_next_visible(); } - case KEY_UP: { + } else { + _go_left(); + } - TreeItem *prev = NULL; - if (!selected_item) { - prev = get_last_item(); - selected_col = 0; - } else { + } else if (p_event->is_action("ui_up") && p_event->is_pressed()) { - prev = selected_item->get_prev_visible(); - if (last_keypress != 0) { - //incr search next - int col; - prev = _search_item_text(prev, incr_search, &col, true, true); - if (!prev) { - accept_event(); - return; - } - } - } + if (!cursor_can_exit_tree) accept_event(); - if (select_mode == SELECT_MULTI) { + _go_up(); - if (!prev) - break; - selected_item = prev; - emit_signal("cell_selected"); - update(); - } else { + } else if (p_event->is_action("ui_down") && p_event->is_pressed()) { - int col = selected_col < 0 ? 0 : selected_col; - while (prev && !prev->cells[col].selectable) - prev = prev->get_prev_visible(); - if (!prev) - break; // do nothing.. - prev->select(col); - } + if (!cursor_can_exit_tree) accept_event(); - ensure_cursor_is_visible(); - accept_event(); + _go_down(); - } break; - case KEY_PAGEDOWN: { + } else if (p_event->is_action("ui_page_down") && p_event->is_pressed()) { - TreeItem *next = NULL; - if (!selected_item) - break; - next = selected_item; + if (!cursor_can_exit_tree) accept_event(); - for (int i = 0; i < 10; i++) { + TreeItem *next = NULL; + if (!selected_item) + return; + next = selected_item; - TreeItem *_n = next->get_next_visible(); - if (_n) { - next = _n; - } else { + for (int i = 0; i < 10; i++) { - break; - } - } - if (next == selected_item) - break; + TreeItem *_n = next->get_next_visible(); + if (_n) { + next = _n; + } else { - if (select_mode == SELECT_MULTI) { + return; + } + } + if (next == selected_item) + return; - selected_item = next; - emit_signal("cell_selected"); - update(); - } else { + if (select_mode == SELECT_MULTI) { - while (next && !next->cells[selected_col].selectable) - next = next->get_next_visible(); - if (!next) - EXIT_BREAK; // do nothing.. - next->select(selected_col); - } + selected_item = next; + emit_signal("cell_selected"); + update(); + } else { - ensure_cursor_is_visible(); - } break; - case KEY_PAGEUP: { + while (next && !next->cells[selected_col].selectable) + next = next->get_next_visible(); + if (!next) { + return; // do nothing.. + } + next->select(selected_col); + } - TreeItem *prev = NULL; - if (!selected_item) - break; - prev = selected_item; + ensure_cursor_is_visible(); + } else if (p_event->is_action("ui_page_up") && p_event->is_pressed()) { - for (int i = 0; i < 10; i++) { + if (!cursor_can_exit_tree) accept_event(); - TreeItem *_n = prev->get_prev_visible(); - if (_n) { - prev = _n; - } else { + TreeItem *prev = NULL; + if (!selected_item) + return; + prev = selected_item; - break; - } - } - if (prev == selected_item) - break; + for (int i = 0; i < 10; i++) { - if (select_mode == SELECT_MULTI) { + TreeItem *_n = prev->get_prev_visible(); + if (_n) { + prev = _n; + } else { - selected_item = prev; - emit_signal("cell_selected"); - update(); - } else { + return; + } + } + if (prev == selected_item) + return; - while (prev && !prev->cells[selected_col].selectable) - prev = prev->get_prev_visible(); - if (!prev) - EXIT_BREAK; // do nothing.. - prev->select(selected_col); - } + if (select_mode == SELECT_MULTI) { - ensure_cursor_is_visible(); + selected_item = prev; + emit_signal("cell_selected"); + update(); + } else { - } break; - case KEY_F2: - case KEY_ENTER: - case KEY_KP_ENTER: { - - if (selected_item) { - //bring up editor if possible - if (!edit_selected()) { - emit_signal("item_activated"); - incr_search.clear(); - } - } - accept_event(); + while (prev && !prev->cells[selected_col].selectable) + prev = prev->get_prev_visible(); + if (!prev) { + return; // do nothing.. + } + prev->select(selected_col); + } + ensure_cursor_is_visible(); + } else if (p_event->is_action("ui_accept") && p_event->is_pressed()) { - } break; - case KEY_SPACE: { - if (select_mode == SELECT_MULTI) { - if (!selected_item) - break; - if (selected_item->is_selected(selected_col)) { - selected_item->deselect(selected_col); - emit_signal("multi_selected", selected_item, selected_col, false); - } else if (selected_item->is_selectable(selected_col)) { - selected_item->select(selected_col); - emit_signal("multi_selected", selected_item, selected_col, true); - } - } - accept_event(); + if (selected_item) { + //bring up editor if possible + if (!edit_selected()) { + emit_signal("item_activated"); + incr_search.clear(); + } + } + accept_event(); + } else if (p_event->is_action("ui_select") && p_event->is_pressed()) { - } break; - default: { + if (select_mode == SELECT_MULTI) { + if (!selected_item) + return; + if (selected_item->is_selected(selected_col)) { + selected_item->deselect(selected_col); + emit_signal("multi_selected", selected_item, selected_col, false); + } else if (selected_item->is_selectable(selected_col)) { + selected_item->select(selected_col); + emit_signal("multi_selected", selected_item, selected_col, true); + } + } + accept_event(); + } + + if (k.is_valid()) { // Incremental search + + if (!k->is_pressed()) + return; + if (k->get_command() || (k->get_shift() && k->get_unicode() == 0) || k->get_metakey()) + return; + if (!root) + return; - if (k->get_unicode() > 0) { + if (hide_root && !root->get_next_visible()) + return; - _do_incr_search(String::chr(k->get_unicode())); - accept_event(); + if (k->get_unicode() > 0) { - return; - } else { - if (k->get_scancode() != KEY_SHIFT) - last_keypress = 0; - } - } break; + _do_incr_search(String::chr(k->get_unicode())); + accept_event(); + + return; + } else { + if (k->get_scancode() != KEY_SHIFT) + last_keypress = 0; } } @@ -3911,6 +3907,8 @@ Tree::Tree() { cache.click_id = -1; cache.click_item = NULL; cache.click_column = 0; + cache.hover_cell = -1; + cache.hover_index = -1; last_keypress = 0; focus_in_id = 0; diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 2a8546a743..5af66c5faa 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -507,6 +507,10 @@ private: ValueEvaluator *evaluator; int _count_selected_items(TreeItem *p_from) const; + void _go_left(); + void _go_right(); + void _go_down(); + void _go_up(); protected: static void _bind_methods(); |