summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/base_button.cpp7
-rw-r--r--scene/gui/color_picker.cpp51
-rw-r--r--scene/gui/color_picker.h3
-rw-r--r--scene/gui/control.cpp50
-rw-r--r--scene/gui/dialogs.h2
-rw-r--r--scene/gui/file_dialog.cpp26
-rw-r--r--scene/gui/file_dialog.h2
-rw-r--r--scene/gui/gradient_edit.cpp7
-rw-r--r--scene/gui/grid_container.cpp28
-rw-r--r--scene/gui/item_list.cpp90
-rw-r--r--scene/gui/item_list.h2
-rw-r--r--scene/gui/line_edit.cpp2
-rw-r--r--scene/gui/option_button.cpp10
-rw-r--r--scene/gui/option_button.h1
-rw-r--r--scene/gui/popup_menu.cpp116
-rw-r--r--scene/gui/rich_text_label.cpp22
-rw-r--r--scene/gui/rich_text_label.h2
-rw-r--r--scene/gui/scroll_bar.cpp56
-rw-r--r--scene/gui/slider.cpp26
-rw-r--r--scene/gui/tab_container.cpp22
-rw-r--r--scene/gui/tab_container.h1
-rw-r--r--scene/gui/tabs.cpp2
-rw-r--r--scene/gui/text_edit.cpp21
-rw-r--r--scene/gui/texture_progress.cpp57
-rw-r--r--scene/gui/tree.cpp520
-rw-r--r--scene/gui/tree.h4
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();