summaryrefslogtreecommitdiff
path: root/scene/gui/item_list.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/item_list.cpp')
-rw-r--r--scene/gui/item_list.cpp518
1 files changed, 331 insertions, 187 deletions
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 9585b4d51d..008109da65 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -43,11 +43,11 @@ void ItemList::_shape(int p_idx) {
} else {
item.text_buf->set_direction((TextServer::Direction)item.text_direction);
}
- item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.opentype_features, (!item.language.is_empty()) ? item.language : TranslationServer::get_singleton()->get_tool_locale());
+ item.text_buf->add_string(item.text, theme_cache.font, theme_cache.font_size, item.language);
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
- item.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND);
+ item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
} else {
- item.text_buf->set_flags(TextServer::BREAK_NONE);
+ item.text_buf->set_break_flags(TextServer::BREAK_NONE);
}
item.text_buf->set_text_overrun_behavior(text_overrun_behavior);
item.text_buf->set_max_lines_visible(max_text_lines);
@@ -63,7 +63,7 @@ int ItemList::add_item(const String &p_item, const Ref<Texture2D> &p_texture, bo
_shape(items.size() - 1);
- update();
+ queue_redraw();
shape_changed = true;
notify_property_list_changed();
return item_id;
@@ -76,18 +76,25 @@ int ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) {
items.push_back(item);
int item_id = items.size() - 1;
- update();
+ queue_redraw();
shape_changed = true;
notify_property_list_changed();
return item_id;
}
void ItemList::set_item_text(int p_idx, const String &p_text) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].text == p_text) {
+ return;
+ }
+
items.write[p_idx].text = p_text;
_shape(p_idx);
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -97,12 +104,15 @@ String ItemList::get_item_text(int p_idx) const {
}
void ItemList::set_item_text_direction(int p_idx, Control::TextDirection p_text_direction) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (items[p_idx].text_direction != p_text_direction) {
items.write[p_idx].text_direction = p_text_direction;
_shape(p_idx);
- update();
+ queue_redraw();
}
}
@@ -111,38 +121,15 @@ Control::TextDirection ItemList::get_item_text_direction(int p_idx) const {
return items[p_idx].text_direction;
}
-void ItemList::clear_item_opentype_features(int p_idx) {
- ERR_FAIL_INDEX(p_idx, items.size());
- items.write[p_idx].opentype_features.clear();
- _shape(p_idx);
- update();
-}
-
-void ItemList::set_item_opentype_feature(int p_idx, const String &p_name, int p_value) {
- ERR_FAIL_INDEX(p_idx, items.size());
- int32_t tag = TS->name_to_tag(p_name);
- if (!items[p_idx].opentype_features.has(tag) || (int)items[p_idx].opentype_features[tag] != p_value) {
- items.write[p_idx].opentype_features[tag] = p_value;
- _shape(p_idx);
- update();
- }
-}
-
-int ItemList::get_item_opentype_feature(int p_idx, const String &p_name) const {
- ERR_FAIL_INDEX_V(p_idx, items.size(), -1);
- int32_t tag = TS->name_to_tag(p_name);
- if (!items[p_idx].opentype_features.has(tag)) {
- return -1;
- }
- return items[p_idx].opentype_features[tag];
-}
-
void ItemList::set_item_language(int p_idx, const String &p_language) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].language != p_language) {
items.write[p_idx].language = p_language;
_shape(p_idx);
- update();
+ queue_redraw();
}
}
@@ -152,6 +139,9 @@ String ItemList::get_item_language(int p_idx) const {
}
void ItemList::set_item_tooltip_enabled(int p_idx, const bool p_enabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].tooltip_enabled = p_enabled;
}
@@ -162,10 +152,17 @@ bool ItemList::is_item_tooltip_enabled(int p_idx) const {
}
void ItemList::set_item_tooltip(int p_idx, const String &p_tooltip) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].tooltip == p_tooltip) {
+ return;
+ }
+
items.write[p_idx].tooltip = p_tooltip;
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -175,10 +172,17 @@ String ItemList::get_item_tooltip(int p_idx) const {
}
void ItemList::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].icon == p_icon) {
+ return;
+ }
+
items.write[p_idx].icon = p_icon;
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -189,10 +193,17 @@ Ref<Texture2D> ItemList::get_item_icon(int p_idx) const {
}
void ItemList::set_item_icon_transposed(int p_idx, const bool p_transposed) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].icon_transposed == p_transposed) {
+ return;
+ }
+
items.write[p_idx].icon_transposed = p_transposed;
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -203,10 +214,17 @@ bool ItemList::is_item_icon_transposed(int p_idx) const {
}
void ItemList::set_item_icon_region(int p_idx, const Rect2 &p_region) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].icon_region == p_region) {
+ return;
+ }
+
items.write[p_idx].icon_region = p_region;
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -217,10 +235,17 @@ Rect2 ItemList::get_item_icon_region(int p_idx) const {
}
void ItemList::set_item_icon_modulate(int p_idx, const Color &p_modulate) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].icon_modulate == p_modulate) {
+ return;
+ }
+
items.write[p_idx].icon_modulate = p_modulate;
- update();
+ queue_redraw();
}
Color ItemList::get_item_icon_modulate(int p_idx) const {
@@ -230,10 +255,17 @@ Color ItemList::get_item_icon_modulate(int p_idx) const {
}
void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_color) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].custom_bg == p_custom_bg_color) {
+ return;
+ }
+
items.write[p_idx].custom_bg = p_custom_bg_color;
- update();
+ queue_redraw();
}
Color ItemList::get_item_custom_bg_color(int p_idx) const {
@@ -243,10 +275,17 @@ Color ItemList::get_item_custom_bg_color(int p_idx) const {
}
void ItemList::set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_color) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].custom_fg == p_custom_fg_color) {
+ return;
+ }
+
items.write[p_idx].custom_fg = p_custom_fg_color;
- update();
+ queue_redraw();
}
Color ItemList::get_item_custom_fg_color(int p_idx) const {
@@ -256,10 +295,17 @@ Color ItemList::get_item_custom_fg_color(int p_idx) const {
}
void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].tag_icon == p_tag_icon) {
+ return;
+ }
+
items.write[p_idx].tag_icon = p_tag_icon;
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -270,6 +316,9 @@ Ref<Texture2D> ItemList::get_item_tag_icon(int p_idx) const {
}
void ItemList::set_item_selectable(int p_idx, bool p_selectable) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].selectable = p_selectable;
@@ -281,10 +330,17 @@ bool ItemList::is_item_selectable(int p_idx) const {
}
void ItemList::set_item_disabled(int p_idx, bool p_disabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].disabled == p_disabled) {
+ return;
+ }
+
items.write[p_idx].disabled = p_disabled;
- update();
+ queue_redraw();
}
bool ItemList::is_item_disabled(int p_idx) const {
@@ -293,10 +349,17 @@ bool ItemList::is_item_disabled(int p_idx) const {
}
void ItemList::set_item_metadata(int p_idx, const Variant &p_metadata) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
+ if (items[p_idx].metadata == p_metadata) {
+ return;
+ }
+
items.write[p_idx].metadata = p_metadata;
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -324,7 +387,7 @@ void ItemList::select(int p_idx, bool p_single) {
items.write[p_idx].selected = true;
}
}
- update();
+ queue_redraw();
}
void ItemList::deselect(int p_idx) {
@@ -336,7 +399,7 @@ void ItemList::deselect(int p_idx) {
} else {
items.write[p_idx].selected = false;
}
- update();
+ queue_redraw();
}
void ItemList::deselect_all() {
@@ -348,7 +411,7 @@ void ItemList::deselect_all() {
items.write[i].selected = false;
}
current = -1;
- update();
+ queue_redraw();
}
bool ItemList::is_selected(int p_idx) const {
@@ -360,11 +423,15 @@ bool ItemList::is_selected(int p_idx) const {
void ItemList::set_current(int p_current) {
ERR_FAIL_INDEX(p_current, items.size());
+ if (current == p_current) {
+ return;
+ }
+
if (select_mode == SELECT_SINGLE) {
select(p_current, true);
} else {
current = p_current;
- update();
+ queue_redraw();
}
}
@@ -384,15 +451,20 @@ void ItemList::move_item(int p_from_idx, int p_to_idx) {
items.remove_at(p_from_idx);
items.insert(p_to_idx, item);
- update();
+ queue_redraw();
shape_changed = true;
notify_property_list_changed();
}
void ItemList::set_item_count(int p_count) {
ERR_FAIL_COND(p_count < 0);
+
+ if (items.size() == p_count) {
+ return;
+ }
+
items.resize(p_count);
- update();
+ queue_redraw();
shape_changed = true;
notify_property_list_changed();
}
@@ -408,7 +480,7 @@ void ItemList::remove_item(int p_idx) {
if (current == p_idx) {
current = -1;
}
- update();
+ queue_redraw();
shape_changed = true;
defer_select_single = -1;
notify_property_list_changed();
@@ -418,7 +490,7 @@ void ItemList::clear() {
items.clear();
current = -1;
ensure_selected_visible = false;
- update();
+ queue_redraw();
shape_changed = true;
defer_select_single = -1;
notify_property_list_changed();
@@ -426,8 +498,13 @@ void ItemList::clear() {
void ItemList::set_fixed_column_width(int p_size) {
ERR_FAIL_COND(p_size < 0);
+
+ if (fixed_column_width == p_size) {
+ return;
+ }
+
fixed_column_width = p_size;
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -436,8 +513,12 @@ int ItemList::get_fixed_column_width() const {
}
void ItemList::set_same_column_width(bool p_enable) {
+ if (same_column_width == p_enable) {
+ return;
+ }
+
same_column_width = p_enable;
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -451,14 +532,14 @@ void ItemList::set_max_text_lines(int p_lines) {
max_text_lines = p_lines;
for (int i = 0; i < items.size(); i++) {
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
- items.write[i].text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND);
+ items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
items.write[i].text_buf->set_max_lines_visible(p_lines);
} else {
- items.write[i].text_buf->set_flags(TextServer::BREAK_NONE);
+ items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE);
}
}
shape_changed = true;
- update();
+ queue_redraw();
}
}
@@ -468,8 +549,13 @@ int ItemList::get_max_text_lines() const {
void ItemList::set_max_columns(int p_amount) {
ERR_FAIL_COND(p_amount < 0);
+
+ if (max_columns == p_amount) {
+ return;
+ }
+
max_columns = p_amount;
- update();
+ queue_redraw();
shape_changed = true;
}
@@ -478,8 +564,12 @@ int ItemList::get_max_columns() const {
}
void ItemList::set_select_mode(SelectMode p_mode) {
+ if (select_mode == p_mode) {
+ return;
+ }
+
select_mode = p_mode;
- update();
+ queue_redraw();
}
ItemList::SelectMode ItemList::get_select_mode() const {
@@ -492,13 +582,13 @@ void ItemList::set_icon_mode(IconMode p_mode) {
icon_mode = p_mode;
for (int i = 0; i < items.size(); i++) {
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
- items.write[i].text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND);
+ items.write[i].text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_EDGE_SPACES);
} else {
- items.write[i].text_buf->set_flags(TextServer::BREAK_NONE);
+ items.write[i].text_buf->set_break_flags(TextServer::BREAK_NONE);
}
}
shape_changed = true;
- update();
+ queue_redraw();
}
}
@@ -506,12 +596,16 @@ ItemList::IconMode ItemList::get_icon_mode() const {
return icon_mode;
}
-void ItemList::set_fixed_icon_size(const Size2 &p_size) {
+void ItemList::set_fixed_icon_size(const Size2i &p_size) {
+ if (fixed_icon_size == p_size) {
+ return;
+ }
+
fixed_icon_size = p_size;
- update();
+ queue_redraw();
}
-Size2 ItemList::get_fixed_icon_size() const {
+Size2i ItemList::get_fixed_icon_size() const {
return fixed_icon_size;
}
@@ -537,6 +631,9 @@ Size2 ItemList::Item::get_icon_size() const {
void ItemList::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
+#define CAN_SELECT(i) (items[i].selectable && !items[i].disabled)
+#define IS_SAME_ROW(i, row) (i / current_columns == row)
+
double prev_scroll = scroll_bar->get_value();
Ref<InputEventMouseMotion> mm = p_event;
@@ -555,11 +652,10 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (mb.is_valid() && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT)) && mb->is_pressed()) {
+ if (mb.is_valid() && mb->is_pressed()) {
search_string = ""; //any mousepress cancels
Vector2 pos = mb->get_position();
- Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
- pos -= bg->get_offset();
+ pos -= theme_cache.panel_style->get_offset();
pos.y += scroll_bar->get_value();
if (is_layout_rtl()) {
@@ -580,10 +676,10 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (closest != -1) {
+ if (closest != -1 && (mb->get_button_index() == MouseButton::LEFT || (allow_rmb_select && mb->get_button_index() == MouseButton::RIGHT))) {
int i = closest;
- if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_pressed()) {
+ if (select_mode == SELECT_MULTI && items[i].selected && mb->is_command_or_control_pressed()) {
deselect(i);
emit_signal(SNAME("multi_selected"), i, false);
@@ -594,55 +690,47 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
SWAP(from, to);
}
for (int j = from; j <= to; j++) {
+ if (!CAN_SELECT(j)) {
+ continue;
+ }
bool selected = !items[j].selected;
select(j, false);
if (selected) {
emit_signal(SNAME("multi_selected"), j, true);
}
}
+ emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index());
- if (mb->get_button_index() == MouseButton::RIGHT) {
- emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position());
- }
} else {
- if (!mb->is_double_click() && !mb->is_command_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) {
+ if (!mb->is_double_click() && !mb->is_command_or_control_pressed() && select_mode == SELECT_MULTI && items[i].selectable && !items[i].disabled && items[i].selected && mb->get_button_index() == MouseButton::LEFT) {
defer_select_single = i;
return;
}
- if (items[i].selected && mb->get_button_index() == MouseButton::RIGHT) {
- emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position());
- } else {
- bool selected = items[i].selected;
-
- select(i, select_mode == SELECT_SINGLE || !mb->is_command_pressed());
+ if (!items[i].selected || allow_reselect) {
+ select(i, select_mode == SELECT_SINGLE || !mb->is_command_or_control_pressed());
- if (!selected || allow_reselect) {
- if (select_mode == SELECT_SINGLE) {
- emit_signal(SNAME("item_selected"), i);
- } else {
- emit_signal(SNAME("multi_selected"), i, true);
- }
+ if (select_mode == SELECT_SINGLE) {
+ emit_signal(SNAME("item_selected"), i);
+ } else {
+ emit_signal(SNAME("multi_selected"), i, true);
}
+ }
- if (mb->get_button_index() == MouseButton::RIGHT) {
- emit_signal(SNAME("item_rmb_selected"), i, get_local_mouse_position());
- } else if (/*select_mode==SELECT_SINGLE &&*/ mb->is_double_click()) {
- emit_signal(SNAME("item_activated"), i);
- }
+ emit_signal(SNAME("item_clicked"), i, get_local_mouse_position(), mb->get_button_index());
+
+ if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) {
+ emit_signal(SNAME("item_activated"), i);
}
}
return;
+ } else if (closest != -1) {
+ emit_signal(SNAME("item_clicked"), closest, get_local_mouse_position(), mb->get_button_index());
+ } else {
+ // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting.
+ emit_signal(SNAME("empty_clicked"), get_local_mouse_position(), mb->get_button_index());
}
- if (mb->get_button_index() == MouseButton::RIGHT) {
- emit_signal(SNAME("rmb_clicked"), mb->get_position());
-
- return;
- }
-
- // Since closest is null, more likely we clicked on empty space, so send signal to interested controls. Allows, for example, implement items deselecting.
- emit_signal(SNAME("nothing_selected"));
}
if (mb.is_valid() && mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
scroll_bar->set_value(scroll_bar->get_value() - scroll_bar->get_page() * mb->get_factor() / 8);
@@ -659,7 +747,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (diff < uint64_t(ProjectSettings::get_singleton()->get("gui/timers/incremental_search_max_interval_msec")) * 2) {
for (int i = current - 1; i >= 0; i--) {
- if (items[i].text.begins_with(search_string)) {
+ if (CAN_SELECT(i) && items[i].text.begins_with(search_string)) {
set_current(i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -675,7 +763,15 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
if (current >= current_columns) {
- set_current(current - current_columns);
+ int next = current - current_columns;
+ while (next >= 0 && !CAN_SELECT(next)) {
+ next = next - current_columns;
+ }
+ if (next < 0) {
+ accept_event();
+ return;
+ }
+ set_current(next);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
emit_signal(SNAME("item_selected"), current);
@@ -689,7 +785,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (diff < uint64_t(ProjectSettings::get_singleton()->get("gui/timers/incremental_search_max_interval_msec")) * 2) {
for (int i = current + 1; i < items.size(); i++) {
- if (items[i].text.begins_with(search_string)) {
+ if (CAN_SELECT(i) && items[i].text.begins_with(search_string)) {
set_current(i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -704,7 +800,15 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
}
if (current < items.size() - current_columns) {
- set_current(current + current_columns);
+ int next = current + current_columns;
+ while (next < items.size() && !CAN_SELECT(next)) {
+ next = next + current_columns;
+ }
+ if (next >= items.size()) {
+ accept_event();
+ return;
+ }
+ set_current(next);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
emit_signal(SNAME("item_selected"), current);
@@ -715,7 +819,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
for (int i = 4; i > 0; i--) {
- if (current - current_columns * i >= 0) {
+ if (current - current_columns * i >= 0 && CAN_SELECT(current - current_columns * i)) {
set_current(current - current_columns * i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -729,7 +833,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
for (int i = 4; i > 0; i--) {
- if (current + current_columns * i < items.size()) {
+ if (current + current_columns * i < items.size() && CAN_SELECT(current + current_columns * i)) {
set_current(current + current_columns * i);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -744,7 +848,16 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
if (current % current_columns != 0) {
- set_current(current - 1);
+ int current_row = current / current_columns;
+ int next = current - 1;
+ while (!CAN_SELECT(next)) {
+ next = next - 1;
+ }
+ if (next < 0 || !IS_SAME_ROW(next, current_row)) {
+ accept_event();
+ return;
+ }
+ set_current(next);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
emit_signal(SNAME("item_selected"), current);
@@ -755,7 +868,16 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
if (current % current_columns != (current_columns - 1) && current + 1 < items.size()) {
- set_current(current + 1);
+ int current_row = current / current_columns;
+ int next = current + 1;
+ while (!CAN_SELECT(next)) {
+ next = next + 1;
+ }
+ if (items.size() <= next || !IS_SAME_ROW(next, current_row)) {
+ accept_event();
+ return;
+ }
+ set_current(next);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
emit_signal(SNAME("item_selected"), current);
@@ -831,11 +953,14 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (scroll_bar->get_value() != prev_scroll) {
accept_event(); //accept event if scroll changed
}
+
+#undef CAN_SELECT
+#undef IS_SAME_ROW
}
void ItemList::ensure_current_is_visible() {
ensure_selected_visible = true;
- update();
+ queue_redraw();
}
static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) {
@@ -854,11 +979,36 @@ static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) {
return Rect2(ofs_x, ofs_y, tex_width, tex_height);
}
+void ItemList::_update_theme_item_cache() {
+ Control::_update_theme_item_cache();
+
+ theme_cache.h_separation = get_theme_constant(SNAME("h_separation"));
+ theme_cache.v_separation = get_theme_constant(SNAME("v_separation"));
+
+ theme_cache.panel_style = get_theme_stylebox(SNAME("panel"));
+ theme_cache.focus_style = get_theme_stylebox(SNAME("focus"));
+
+ theme_cache.font = get_theme_font(SNAME("font"));
+ theme_cache.font_size = get_theme_font_size(SNAME("font_size"));
+ theme_cache.font_color = get_theme_color(SNAME("font_color"));
+ theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color"));
+ theme_cache.font_outline_size = get_theme_constant(SNAME("outline_size"));
+ theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color"));
+
+ theme_cache.line_separation = get_theme_constant(SNAME("line_separation"));
+ theme_cache.icon_margin = get_theme_constant(SNAME("icon_margin"));
+ theme_cache.selected_style = get_theme_stylebox(SNAME("selected"));
+ theme_cache.selected_focus_style = get_theme_stylebox(SNAME("selected_focus"));
+ theme_cache.cursor_style = get_theme_stylebox(SNAME("cursor_unfocused"));
+ theme_cache.cursor_focus_style = get_theme_stylebox(SNAME("cursor"));
+ theme_cache.guide_color = get_theme_color(SNAME("guide_color"));
+}
+
void ItemList::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_RESIZED: {
shape_changed = true;
- update();
+ queue_redraw();
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
@@ -868,45 +1018,36 @@ void ItemList::_notification(int p_what) {
_shape(i);
}
shape_changed = true;
- update();
+ queue_redraw();
} break;
case NOTIFICATION_DRAW: {
- Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
-
int mw = scroll_bar->get_minimum_size().x;
scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw);
scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP));
- scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM));
+ scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, theme_cache.panel_style->get_margin(SIDE_TOP));
+ scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -theme_cache.panel_style->get_margin(SIDE_BOTTOM));
Size2 size = get_size();
+ int width = size.width - theme_cache.panel_style->get_minimum_size().width;
- int width = size.width - bg->get_minimum_size().width;
- if (scroll_bar->is_visible()) {
- width -= mw;
- }
-
- draw_style_box(bg, Rect2(Point2(), size));
+ draw_style_box(theme_cache.panel_style, Rect2(Point2(), size));
- int hseparation = get_theme_constant(SNAME("hseparation"));
- int vseparation = get_theme_constant(SNAME("vseparation"));
- int icon_margin = get_theme_constant(SNAME("icon_margin"));
- int line_separation = get_theme_constant(SNAME("line_separation"));
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
+ Ref<StyleBox> sbsel;
+ Ref<StyleBox> cursor;
- Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected"));
- Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused"));
+ if (has_focus()) {
+ sbsel = theme_cache.selected_focus_style;
+ cursor = theme_cache.cursor_focus_style;
+ } else {
+ sbsel = theme_cache.selected_style;
+ cursor = theme_cache.cursor_style;
+ }
bool rtl = is_layout_rtl();
- Color guide_color = get_theme_color(SNAME("guide_color"));
- Color font_color = get_theme_color(SNAME("font_color"));
- Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
-
if (has_focus()) {
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true);
- draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size));
+ draw_style_box(theme_cache.focus_style, Rect2(Point2(), size));
RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false);
}
@@ -925,9 +1066,9 @@ void ItemList::_notification(int p_what) {
if (!items[i].text.is_empty()) {
if (icon_mode == ICON_MODE_TOP) {
- minsize.y += icon_margin;
+ minsize.y += theme_cache.icon_margin;
} else {
- minsize.x += icon_margin;
+ minsize.x += theme_cache.icon_margin;
}
}
}
@@ -945,7 +1086,7 @@ void ItemList::_notification(int p_what) {
if (icon_mode == ICON_MODE_TOP) {
minsize.x = MAX(minsize.x, s.width);
if (max_text_lines > 0) {
- minsize.y += s.height + line_separation * max_text_lines;
+ minsize.y += s.height + theme_cache.line_separation * max_text_lines;
} else {
minsize.y += s.height;
}
@@ -962,13 +1103,13 @@ void ItemList::_notification(int p_what) {
max_column_width = MAX(max_column_width, minsize.x);
// elements need to adapt to the selected size
- minsize.y += vseparation;
- minsize.x += hseparation;
+ minsize.y += theme_cache.v_separation;
+ minsize.x += theme_cache.h_separation;
items.write[i].rect_cache.size = minsize;
items.write[i].min_rect_cache.size = minsize;
}
- int fit_size = size.x - bg->get_minimum_size().width - mw;
+ int fit_size = size.x - theme_cache.panel_style->get_minimum_size().width - mw;
//2-attempt best fit
current_columns = 0x7FFFFFFF;
@@ -996,11 +1137,11 @@ void ItemList::_notification(int p_what) {
}
items.write[i].rect_cache.position = ofs;
max_h = MAX(max_h, items[i].rect_cache.size.y);
- ofs.x += items[i].rect_cache.size.x + hseparation;
+ ofs.x += items[i].rect_cache.size.x + theme_cache.h_separation;
col++;
if (col == current_columns) {
if (i < items.size() - 1) {
- separators.push_back(ofs.y + max_h + vseparation / 2);
+ separators.push_back(ofs.y + max_h + theme_cache.v_separation / 2);
}
for (int j = i; j >= 0 && col > 0; j--, col--) {
@@ -1008,7 +1149,7 @@ void ItemList::_notification(int p_what) {
}
ofs.x = 0;
- ofs.y += max_h + vseparation;
+ ofs.y += max_h + theme_cache.v_separation;
col = 0;
max_h = 0;
}
@@ -1019,10 +1160,10 @@ void ItemList::_notification(int p_what) {
}
if (all_fit) {
- float page = MAX(0, size.height - bg->get_minimum_size().height);
+ float page = MAX(0, size.height - theme_cache.panel_style->get_minimum_size().height);
float max = MAX(page, ofs.y + max_h);
if (auto_height) {
- auto_height_value = ofs.y + max_h + bg->get_minimum_size().height;
+ auto_height_value = ofs.y + max_h + theme_cache.panel_style->get_minimum_size().height;
}
scroll_bar->set_max(max);
scroll_bar->set_page(page);
@@ -1044,6 +1185,10 @@ void ItemList::_notification(int p_what) {
shape_changed = false;
}
+ if (scroll_bar->is_visible()) {
+ width -= mw;
+ }
+
//ensure_selected_visible needs to be checked before we draw the list.
if (ensure_selected_visible && current >= 0 && current < items.size()) {
Rect2 r = items[current].rect_cache;
@@ -1059,7 +1204,7 @@ void ItemList::_notification(int p_what) {
ensure_selected_visible = false;
- Vector2 base_ofs = bg->get_offset();
+ Vector2 base_ofs = theme_cache.panel_style->get_offset();
base_ofs.y -= int(scroll_bar->get_value());
const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there
@@ -1103,10 +1248,10 @@ void ItemList::_notification(int p_what) {
if (items[i].selected) {
Rect2 r = rcache;
r.position += base_ofs;
- r.position.y -= vseparation / 2;
- r.size.y += vseparation;
- r.position.x -= hseparation / 2;
- r.size.x += hseparation;
+ r.position.y -= theme_cache.v_separation / 2;
+ r.size.y += theme_cache.v_separation;
+ r.position.x -= theme_cache.h_separation / 2;
+ r.size.x += theme_cache.h_separation;
if (rtl) {
r.position.x = size.width - r.position.x - r.size.x;
@@ -1119,10 +1264,10 @@ void ItemList::_notification(int p_what) {
r.position += base_ofs;
// Size rect to make the align the temperature colors
- r.position.y -= vseparation / 2;
- r.size.y += vseparation;
- r.position.x -= hseparation / 2;
- r.size.x += hseparation;
+ r.position.y -= theme_cache.v_separation / 2;
+ r.size.y += theme_cache.v_separation;
+ r.position.x -= theme_cache.h_separation / 2;
+ r.size.x += theme_cache.h_separation;
if (rtl) {
r.position.x = size.width - r.position.x - r.size.x;
@@ -1148,11 +1293,11 @@ void ItemList::_notification(int p_what) {
if (icon_mode == ICON_MODE_TOP) {
pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2);
- pos.y += icon_margin;
- text_ofs.y = icon_size.height + icon_margin * 2;
+ pos.y += theme_cache.icon_margin;
+ text_ofs.y = icon_size.height + theme_cache.icon_margin * 2;
} else {
pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2);
- text_ofs.x = icon_size.width + icon_margin;
+ text_ofs.x = icon_size.width + theme_cache.icon_margin;
}
Rect2 draw_rect = Rect2(pos, icon_size);
@@ -1203,7 +1348,7 @@ void ItemList::_notification(int p_what) {
max_len = size2.x;
}
- Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color);
+ Color modulate = items[i].selected ? theme_cache.font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : theme_cache.font_color);
if (items[i].disabled) {
modulate.a *= 0.5;
}
@@ -1218,8 +1363,8 @@ void ItemList::_notification(int p_what) {
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- if (outline_size > 0 && font_outline_color.a > 0) {
- items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color);
}
items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
@@ -1241,7 +1386,7 @@ void ItemList::_notification(int p_what) {
text_ofs.x = size.width - text_ofs.x - max_len;
}
- items.write[i].text_buf->set_width(max_len);
+ items.write[i].text_buf->set_width(width - text_ofs.x);
if (rtl) {
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
@@ -1249,21 +1394,23 @@ void ItemList::_notification(int p_what) {
items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT);
}
- if (outline_size > 0 && font_outline_color.a > 0) {
- items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ if (theme_cache.font_outline_size > 0 && theme_cache.font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, theme_cache.font_outline_size, theme_cache.font_outline_color);
}
- items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
+ if (width - text_ofs.x > 0) {
+ items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
+ }
}
}
if (select_mode == SELECT_MULTI && i == current) {
Rect2 r = rcache;
r.position += base_ofs;
- r.position.y -= vseparation / 2;
- r.size.y += vseparation;
- r.position.x -= hseparation / 2;
- r.size.x += hseparation;
+ r.position.y -= theme_cache.v_separation / 2;
+ r.size.y += theme_cache.v_separation;
+ r.position.x -= theme_cache.h_separation / 2;
+ r.size.x += theme_cache.h_separation;
if (rtl) {
r.position.x = size.width - r.position.x - r.size.x;
@@ -1295,20 +1442,19 @@ void ItemList::_notification(int p_what) {
}
const int y = base_ofs.y + separators[i];
- draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color);
+ draw_line(Vector2(theme_cache.panel_style->get_margin(SIDE_LEFT), y), Vector2(width, y), theme_cache.guide_color);
}
} break;
}
}
void ItemList::_scroll_changed(double) {
- update();
+ queue_redraw();
}
int ItemList::get_item_at_position(const Point2 &p_pos, bool p_exact) const {
Vector2 pos = p_pos;
- Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
- pos -= bg->get_offset();
+ pos -= theme_cache.panel_style->get_offset();
pos.y += scroll_bar->get_value();
if (is_layout_rtl()) {
@@ -1345,8 +1491,7 @@ bool ItemList::is_pos_at_end_of_items(const Point2 &p_pos) const {
}
Vector2 pos = p_pos;
- Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
- pos -= bg->get_offset();
+ pos -= theme_cache.panel_style->get_offset();
pos.y += scroll_bar->get_value();
if (is_layout_rtl()) {
@@ -1377,7 +1522,7 @@ String ItemList::get_tooltip(const Point2 &p_pos) const {
void ItemList::sort_items_by_text() {
items.sort();
- update();
+ queue_redraw();
shape_changed = true;
if (select_mode == SELECT_SINGLE) {
@@ -1459,27 +1604,31 @@ void ItemList::set_autoscroll_to_bottom(const bool p_enable) {
}
void ItemList::set_auto_height(bool p_enable) {
+ if (auto_height == p_enable) {
+ return;
+ }
+
auto_height = p_enable;
shape_changed = true;
- update();
+ queue_redraw();
}
bool ItemList::has_auto_height() const {
return auto_height;
}
-void ItemList::set_text_overrun_behavior(TextParagraph::OverrunBehavior p_behavior) {
+void ItemList::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) {
if (text_overrun_behavior != p_behavior) {
text_overrun_behavior = p_behavior;
for (int i = 0; i < items.size(); i++) {
items.write[i].text_buf->set_text_overrun_behavior(p_behavior);
}
shape_changed = true;
- update();
+ queue_redraw();
}
}
-TextParagraph::OverrunBehavior ItemList::get_text_overrun_behavior() const {
+TextServer::OverrunBehavior ItemList::get_text_overrun_behavior() const {
return text_overrun_behavior;
}
@@ -1574,10 +1723,6 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_text_direction", "idx", "direction"), &ItemList::set_item_text_direction);
ClassDB::bind_method(D_METHOD("get_item_text_direction", "idx"), &ItemList::get_item_text_direction);
- ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "idx", "tag", "value"), &ItemList::set_item_opentype_feature);
- ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "idx", "tag"), &ItemList::get_item_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "idx"), &ItemList::clear_item_opentype_features);
-
ClassDB::bind_method(D_METHOD("set_item_language", "idx", "language"), &ItemList::set_item_language);
ClassDB::bind_method(D_METHOD("get_item_language", "idx"), &ItemList::get_item_language);
@@ -1681,11 +1826,11 @@ void ItemList::_bind_methods() {
ADD_GROUP("Columns", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_columns", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), "set_max_columns", "get_max_columns");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "same_column_width"), "set_same_column_width", "is_same_column_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_column_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater"), "set_fixed_column_width", "get_fixed_column_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_column_width", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:px"), "set_fixed_column_width", "get_fixed_column_width");
ADD_GROUP("Icon", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_mode", PROPERTY_HINT_ENUM, "Top,Left"), "set_icon_mode", "get_icon_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "icon_scale"), "set_icon_scale", "get_icon_scale");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "fixed_icon_size"), "set_fixed_icon_size", "get_fixed_icon_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "fixed_icon_size", PROPERTY_HINT_NONE, "suffix:px"), "set_fixed_icon_size", "get_fixed_icon_size");
BIND_ENUM_CONSTANT(ICON_MODE_TOP);
BIND_ENUM_CONSTANT(ICON_MODE_LEFT);
@@ -1694,11 +1839,10 @@ void ItemList::_bind_methods() {
BIND_ENUM_CONSTANT(SELECT_MULTI);
ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index")));
- ADD_SIGNAL(MethodInfo("item_rmb_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position")));
+ ADD_SIGNAL(MethodInfo("empty_clicked", PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index")));
+ ADD_SIGNAL(MethodInfo("item_clicked", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::VECTOR2, "at_position"), PropertyInfo(Variant::INT, "mouse_button_index")));
ADD_SIGNAL(MethodInfo("multi_selected", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "selected")));
ADD_SIGNAL(MethodInfo("item_activated", PropertyInfo(Variant::INT, "index")));
- ADD_SIGNAL(MethodInfo("rmb_clicked", PropertyInfo(Variant::VECTOR2, "at_position")));
- ADD_SIGNAL(MethodInfo("nothing_selected"));
GLOBAL_DEF("gui/timers/incremental_search_max_interval_msec", 2000);
ProjectSettings::get_singleton()->set_custom_property_info("gui/timers/incremental_search_max_interval_msec", PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater")); // No negative numbers