Skip to content

Commit

Permalink
Fix Tree Mouse hover position
Browse files Browse the repository at this point in the history
  • Loading branch information
Hilderin committed Feb 14, 2025
1 parent 750640c commit 73051b0
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 161 deletions.
211 changes: 52 additions & 159 deletions scene/gui/tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2874,7 +2874,7 @@ void Tree::_range_click_timeout() {

int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width;
if (v_scroll->is_visible()) {
x_limit -= v_scroll->get_minimum_size().width;
x_limit -= v_scroll->get_minimum_size().width + theme_cache.scrollbar_h_separation;
}

cache.rtl = is_layout_rtl();
Expand Down Expand Up @@ -2989,11 +2989,16 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
}

// Cell button detection code.
// The first half of the button margin will be applied to the left button,
// and the other half to the right button.
const int offset_button_width = theme_cache.button_margin / 2;
for (int j = c.buttons.size() - 1; j >= 0; j--) {
Ref<Texture2D> b = c.buttons[j].texture;
int w = b->get_size().width + theme_cache.button_pressed->get_minimum_size().width;

if (x > col_width - w) {
// Since col_width is based on the size of the column and the size of the button,
// a -1 is needed to compare the x position which is zero based.
if (x >= col_width - w - offset_button_width - 1) {
if (c.buttons[j].disabled) {
pressed_button = -1;
cache.click_type = Cache::CLICK_NONE;
Expand Down Expand Up @@ -3924,7 +3929,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {

int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width;
if (v_scroll->is_visible()) {
x_limit -= v_scroll->get_minimum_size().width;
x_limit -= v_scroll->get_minimum_size().width + theme_cache.scrollbar_h_separation;
}

cache.rtl = is_layout_rtl();
Expand Down Expand Up @@ -4059,115 +4064,36 @@ void Tree::_determine_hovered_item() {
}

// Determine hover on rows and items.
if (root && is_mouse_hovering) {
Point2 mpos = hovered_pos;
if (rtl) {
mpos.x = get_size().width - mpos.x;
}
mpos -= theme_cache.panel_style->get_offset();
mpos.y -= _get_title_button_height();
if (mpos.y >= 0) {
if (h_scroll->is_visible_in_tree()) {
mpos.x += h_scroll->get_value();
}
if (v_scroll->is_visible_in_tree()) {
mpos.y += v_scroll->get_value();
}

int col, h, section;
TreeItem *it = _find_item_at_pos(root, mpos, col, h, section);

// Find possible hovered button in cell.
int col_button_index = -1;

Point2 cpos = mpos;

if (it) {
const TreeItem::Cell &c = it->cells[col];
int col_width = get_column_width(col);

// In the first column, tree nesting indent impacts the leftmost possible buttons position
// and the clickable area of the folding arrow.
int col_indent = 0;
if (col == 0) {
col_indent = _get_item_h_offset(it);
}

// Compute total width of buttons block including spacings.
int buttons_width = 0;
for (int j = c.buttons.size() - 1; j >= 0; j--) {
Ref<Texture2D> b = c.buttons[j].texture;
Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size();
buttons_width += size.width + theme_cache.button_margin;
}

// Adjust when buttons are shifted left into view so that they remain visible even
// if part of the cell is beyond the right border due to horizontal scrolling and
// a long string in one of the items. This matches the drawing & click handling algorithms
// that are based on recursion.
int clamped_column_offset = 0;
int col_left = 0;

for (int i = 0; i < col; i++) {
int i_col_w = get_column_width(i);
cpos.x -= i_col_w;
col_left += i_col_w;
}
col_left -= theme_cache.offset.x;

// Compute buttons offset that makes them visible, in comparison to what would be their
// natural position that would cut them off.
if (!rtl) {
const Rect2 content_rect = _get_content_rect();
int cw = content_rect.size.width;
int col_right = col_left + col_width;
if (col_right > cw) {
clamped_column_offset = col_right - cw - theme_cache.scrollbar_h_separation;
int max_clamp_offset = col_width - col_indent - buttons_width;
if (clamped_column_offset > max_clamp_offset) {
clamped_column_offset = max_clamp_offset;
}
}
}
col_width -= clamped_column_offset;

// Find the actual button under coordinates.
for (int j = c.buttons.size() - 1; j >= 0; j--) {
Ref<Texture2D> b = c.buttons[j].texture;
Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size();
if (cpos.x > col_width - size.width && col_button_index == -1) {
col_button_index = j;
}
col_width -= size.width + theme_cache.button_margin;
}
if (root && is_mouse_hovering && hovered_pos.y >= 0) {
TreeItem *it;
int col, section, col_button_index;
_find_button_at_pos(hovered_pos, it, col, col_button_index, section);

if (drop_mode_flags) {
if (it != drop_mode_over) {
drop_mode_over = it;
queue_redraw();
}

if (drop_mode_flags) {
if (it != drop_mode_over) {
drop_mode_over = it;
queue_redraw();
}
if (it && section != drop_mode_section) {
drop_mode_section = section;
queue_redraw();
}
if (it && section != drop_mode_section) {
drop_mode_section = section;
queue_redraw();
}
}

cache.hover_item = it;
cache.hover_column = col;
cache.hover_button_index_in_column = col_button_index;
cache.hover_item = it;
cache.hover_column = col;
cache.hover_button_index_in_column = col_button_index;

if (it != old_item || col != old_column) {
if (old_item && old_column >= old_item->cells.size()) {
// Columns may have changed since last redraw().
if (it != old_item || col != old_column) {
if (old_item && old_column >= old_item->cells.size()) {
// Columns may have changed since last redraw().
queue_redraw();
} else {
// Only need to update if mouse enters/exits a button.
bool was_over_button = old_item && old_item->cells[old_column].custom_button;
bool is_over_button = it && it->cells[col].custom_button;
if (was_over_button || is_over_button) {
queue_redraw();
} else {
// Only need to update if mouse enters/exits a button.
bool was_over_button = old_item && old_item->cells[old_column].custom_button;
bool is_over_button = it && it->cells[col].custom_button;
if (was_over_button || is_over_button) {
queue_redraw();
}
}
}
}
Expand Down Expand Up @@ -5107,46 +5033,6 @@ int Tree::get_item_offset(TreeItem *p_item) const {
return -1; // Not found.
}

int Tree::_get_item_h_offset(TreeItem *p_item) const {
TreeItem *it = root;
int nesting_level = 0;
if (!it) {
return 0;
}

while (true) {
if (it == p_item) {
if (!hide_root) {
nesting_level += 1;
}
if (hide_folding) {
nesting_level -= 1;
}
return nesting_level * theme_cache.item_margin;
}

if (it->first_child && !it->collapsed) {
it = it->first_child;
nesting_level += 1;

} else if (it->next) {
it = it->next;
} else {
while (!it->next) {
it = it->parent;
nesting_level -= 1;
if (it == nullptr) {
return 0;
}
}

it = it->next;
}
}

return -1; // Not found.
}

void Tree::ensure_cursor_is_visible() {
if (!is_inside_tree()) {
return;
Expand Down Expand Up @@ -5591,24 +5477,25 @@ TreeItem *Tree::_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_
// When on a button, r_index is valid.
// When on an item, both r_item and r_column are valid.
// Otherwise, all output arguments are invalid.
void Tree::_find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_column, int &r_index) const {
void Tree::_find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_column, int &r_index, int &r_section) const {
r_item = nullptr;
r_column = -1;
r_index = -1;
r_section = -1;

if (!root) {
return;
}

Point2 pos = p_pos - theme_cache.panel_style->get_offset();
Point2 pos = p_pos;
if (cache.rtl) {
pos.x = get_size().width - pos.x - 1;
}
pos -= theme_cache.panel_style->get_offset();
pos.y -= _get_title_button_height();
if (pos.y < 0) {
return;
}

if (cache.rtl) {
pos.x = get_size().width - pos.x;
}
pos += theme_cache.offset; // Scrolling.

int col, h, section;
Expand All @@ -5619,6 +5506,7 @@ void Tree::_find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_co

r_item = it;
r_column = col;
r_section = section;

const TreeItem::Cell &c = it->cells[col];
if (c.buttons.is_empty()) {
Expand All @@ -5627,7 +5515,7 @@ void Tree::_find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_co

int x_limit = get_size().width - theme_cache.panel_style->get_minimum_size().width + theme_cache.offset.x;
if (v_scroll->is_visible_in_tree()) {
x_limit -= v_scroll->get_minimum_size().width;
x_limit -= v_scroll->get_minimum_size().width + theme_cache.scrollbar_h_separation;
}

for (int i = 0; i < col; i++) {
Expand Down Expand Up @@ -5656,10 +5544,15 @@ void Tree::_find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_co
x_check = MAX(buttons_area_min, MIN(get_column_width(col), x_limit));
}

// The first half of the button margin will be applied to the left button,
// and the other half to the right button.
const int offset_button_width = theme_cache.button_margin / 2;
for (int i = c.buttons.size() - 1; i >= 0; i--) {
Ref<Texture2D> b = c.buttons[i].texture;
Size2 size = b->get_size() + theme_cache.button_pressed->get_minimum_size();
if (pos.x > x_check - size.width) {
// Since x_check is based on the size of the column and the size of the button,
// a -1 is needed to compare the x position which is zero based.
if (pos.x >= x_check - size.width - offset_button_width - 1) {
x_limit -= theme_cache.item_margin;
r_index = i;
return;
Expand Down Expand Up @@ -5785,8 +5678,8 @@ int Tree::get_button_id_at_position(const Point2 &p_pos) const {
}

TreeItem *it;
int col, index;
_find_button_at_pos(p_pos, it, col, index);
int col, index, section;
_find_button_at_pos(p_pos, it, col, index, section);

if (index == -1) {
return -1;
Expand All @@ -5802,8 +5695,8 @@ String Tree::get_tooltip(const Point2 &p_pos) const {
}

TreeItem *it;
int col, index;
_find_button_at_pos(p_pos, it, col, index);
int col, index, section;
_find_button_at_pos(p_pos, it, col, index, section);

if (index != -1) {
return it->cells[col].buttons[index].tooltip;
Expand Down
3 changes: 1 addition & 2 deletions scene/gui/tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,9 +670,8 @@ class Tree : public Control {
TreeItem *_search_item_text(TreeItem *p_at, const String &p_find, int *r_col, bool p_selectable, bool p_backwards = false);

TreeItem *_find_item_at_pos(TreeItem *p_item, const Point2 &p_pos, int &r_column, int &r_height, int &r_section) const;
int _get_item_h_offset(TreeItem *p_item) const;

void _find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_column, int &r_index) const;
void _find_button_at_pos(const Point2 &p_pos, TreeItem *&r_item, int &r_column, int &r_index, int &r_section) const;

/* float drag_speed;
float drag_accum;
Expand Down

0 comments on commit 73051b0

Please sign in to comment.