فهرست منبع

Merge pull request #102842 from Hilderin/fix-tree-mouse-hover

Fix Tree Mouse hover position
Rémi Verschelde 6 ماه پیش
والد
کامیت
93d2706930
2فایلهای تغییر یافته به همراه167 افزوده شده و 279 حذف شده
  1. 156 277
      scene/gui/tree.cpp
  2. 11 2
      scene/gui/tree.h

+ 156 - 277
scene/gui/tree.cpp

@@ -2872,10 +2872,7 @@ void Tree::_range_click_timeout() {
 		Ref<InputEventMouseButton> mb;
 		mb.instantiate();
 
-		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;
-		}
+		int x_limit = _get_content_rect().size.x;
 
 		cache.rtl = is_layout_rtl();
 
@@ -2909,6 +2906,10 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
 		// Skip any processing of invisible items.
 		return 0;
 	}
+	if (p_pos.x > x_limit) {
+		// Inside the scroll area.
+		return -1;
+	}
 
 	int item_h = compute_item_height(p_item) + theme_cache.v_separation;
 
@@ -2930,96 +2931,42 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
 			return -1;
 		}
 
-		int x = p_pos.x;
-		/* find clicked column */
-		int col = -1;
-		int col_ofs = 0;
-		int col_width = 0;
-
-		int limit_w = x_limit;
-
-		for (int i = 0; i < columns.size(); i++) {
-			col_width = get_column_width(i);
+		FindColumnButtonResult find_result = _find_column_and_button_at_pos(p_pos.x, p_item, x_ofs, x_limit);
 
-			if (p_item->cells[i].expand_right) {
-				int plus = 1;
-				while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) {
-					col_width += theme_cache.h_separation;
-					col_width += get_column_width(i + plus);
-					plus++;
-				}
-			}
-
-			if (x > col_width) {
-				col_ofs += col_width;
-				x -= col_width;
-				limit_w -= col_width;
-				continue;
-			}
-
-			col = i;
-			break;
-		}
-
-		if (col == -1) {
+		if (find_result.column_index == -1) {
 			return -1;
-		} else if (col == 0) {
-			int margin = x_ofs + theme_cache.item_margin; //-theme_cache.h_separation;
-			//int lm = theme_cache.panel_style->get_margin(SIDE_LEFT);
-			col_width -= margin;
-			limit_w -= margin;
-			col_ofs += margin;
-			x -= margin;
-		} else {
-			col_width -= theme_cache.h_separation;
-			limit_w -= theme_cache.h_separation;
-			x -= theme_cache.h_separation;
 		}
 
+		int col = find_result.column_index;
+		int col_width = find_result.column_width;
+		int col_ofs = find_result.column_offset;
+		int x = find_result.pos_x;
 		const TreeItem::Cell &c = p_item->cells[col];
 
-		if (!cache.rtl && !p_item->cells[col].buttons.is_empty()) {
-			int button_w = 0;
-			for (int j = p_item->cells[col].buttons.size() - 1; j >= 0; j--) {
-				Ref<Texture2D> b = p_item->cells[col].buttons[j].texture;
-				button_w += b->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin;
+		if (find_result.button_index >= 0) {
+			if (c.buttons[find_result.button_index].disabled) {
+				pressed_button = -1;
+				cache.click_type = Cache::CLICK_NONE;
+				return -1;
 			}
 
-			col_width = MAX(button_w, MIN(limit_w, col_width));
-		}
-
-		// Cell button detection code.
-		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) {
-				if (c.buttons[j].disabled) {
-					pressed_button = -1;
-					cache.click_type = Cache::CLICK_NONE;
-					return -1;
-				}
-
-				// Make sure the click is correct.
-				const Point2 click_pos = get_local_mouse_position();
-				if (!get_item_at_position(click_pos)) {
-					pressed_button = -1;
-					cache.click_type = Cache::CLICK_NONE;
-					return -1;
-				}
-
-				pressed_button = j;
-				cache.click_type = Cache::CLICK_BUTTON;
-				cache.click_index = j;
-				cache.click_id = c.buttons[j].id;
-				cache.click_item = p_item;
-				cache.click_column = col;
-				cache.click_pos = click_pos;
-				queue_redraw();
+			// Make sure the click is correct.
+			const Point2 click_pos = get_local_mouse_position();
+			if (!get_item_at_position(click_pos)) {
+				pressed_button = -1;
+				cache.click_type = Cache::CLICK_NONE;
 				return -1;
 			}
 
-			col_width -= w + theme_cache.button_margin;
+			pressed_button = find_result.button_index;
+			cache.click_type = Cache::CLICK_BUTTON;
+			cache.click_index = find_result.button_index;
+			cache.click_id = c.buttons[find_result.button_index].id;
+			cache.click_item = p_item;
+			cache.click_column = col;
+			cache.click_pos = click_pos;
+			queue_redraw();
+			return -1;
 		}
 
 		if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) {
@@ -3790,7 +3737,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
 					mb->get_button_index() == MouseButton::RIGHT) {
 				Point2 pos = mb->get_position();
 				if (rtl) {
-					pos.x = get_size().width - pos.x;
+					pos.x = get_size().width - pos.x - 1;
 				}
 				pos -= theme_cache.panel_style->get_offset();
 				if (show_column_titles) {
@@ -3890,7 +3837,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
 
 				Point2 pos = mb->get_position();
 				if (rtl) {
-					pos.x = get_size().width - pos.x;
+					pos.x = get_size().width - pos.x - 1;
 				}
 				pos -= bg->get_offset();
 				cache.click_type = Cache::CLICK_NONE;
@@ -3922,10 +3869,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
 				pressing_for_editor = false;
 				propagate_mouse_activated = false;
 
-				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;
-				}
+				int x_limit = _get_content_rect().size.x;
 
 				cache.rtl = is_layout_rtl();
 				blocked++;
@@ -4028,7 +3972,7 @@ void Tree::_determine_hovered_item() {
 
 	Point2 pos = hovered_pos;
 	if (rtl) {
-		pos.x = get_size().width - pos.x;
+		pos.x = get_size().width - pos.x - 1;
 	}
 	pos -= theme_cache.panel_style->get_offset();
 
@@ -4059,115 +4003,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();
-					}
 				}
 			}
 		}
@@ -5107,46 +4972,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;
@@ -5591,25 +5416,33 @@ 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;
 	}
+	pos += theme_cache.offset; // Scrolling.
 
-	if (cache.rtl) {
-		pos.x = get_size().width - pos.x;
+	int x_limit = _get_content_rect().size.x + theme_cache.offset.width;
+
+	if (pos.x > x_limit) {
+		// Inside the scroll area.
+		return;
 	}
-	pos += theme_cache.offset; // Scrolling.
 
 	int col, h, section;
 	TreeItem *it = _find_item_at_pos(root, pos, col, h, section);
@@ -5619,53 +5452,99 @@ 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()) {
 		return;
 	}
 
-	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;
-	}
+	// Search the button index.
+	FindColumnButtonResult result = _find_column_and_button_at_pos(pos.x, it, 0, x_limit);
+	r_index = result.button_index;
+}
 
-	for (int i = 0; i < col; i++) {
-		const int col_w = get_column_width(i);
-		pos.x -= col_w;
-		x_limit -= col_w;
-	}
+Tree::FindColumnButtonResult Tree::_find_column_and_button_at_pos(int p_x, const TreeItem *p_item, int p_x_ofs, int p_x_limit) const {
+	int x = p_x;
 
-	int x_check;
-	if (cache.rtl) {
-		x_check = get_column_width(col);
-	} else {
-		// Right edge of the buttons area, relative to the start of the column.
-		int buttons_area_min = 0;
-		if (col == 0) {
-			// Content of column 0 should take indentation into account.
-			for (TreeItem *current = it; current && (current != root || !hide_root); current = current->parent) {
-				buttons_area_min += theme_cache.item_margin;
+	int col = -1;
+	int col_ofs = 0;
+	int col_width = 0;
+
+	int limit_w = p_x_limit;
+
+	FindColumnButtonResult result;
+
+	for (int i = 0; i < columns.size(); i++) {
+		col_width = get_column_width(i);
+
+		if (p_item->cells[i].expand_right) {
+			int plus = 1;
+			while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) {
+				col_width += theme_cache.h_separation;
+				col_width += get_column_width(i + plus);
+				plus++;
 			}
 		}
-		for (int i = c.buttons.size() - 1; i >= 0; i--) {
-			Ref<Texture2D> b = c.buttons[i].texture;
-			buttons_area_min += b->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin;
+
+		if (x > col_width) {
+			col_ofs += col_width;
+			x -= col_width;
+			limit_w -= col_width;
+			continue;
 		}
 
-		x_check = MAX(buttons_area_min, MIN(get_column_width(col), x_limit));
+		col = i;
+		break;
 	}
 
-	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) {
-			x_limit -= theme_cache.item_margin;
-			r_index = i;
-			return;
+	if (col >= 0) {
+		if (col == 0) {
+			int margin = p_x_ofs + theme_cache.item_margin;
+			col_width -= margin;
+			limit_w -= margin;
+			col_ofs += margin;
+			x -= margin;
+		} else {
+			col_width -= theme_cache.h_separation;
+			limit_w -= theme_cache.h_separation;
+			x -= theme_cache.h_separation;
+		}
+
+		if (!cache.rtl && !p_item->cells[col].buttons.is_empty()) {
+			int button_w = 0;
+			for (int j = p_item->cells[col].buttons.size() - 1; j >= 0; j--) {
+				const Ref<Texture2D> &b = p_item->cells[col].buttons[j].texture;
+				button_w += b->get_size().width + theme_cache.button_pressed->get_minimum_size().width + theme_cache.button_margin;
+			}
+
+			col_width = MAX(button_w, MIN(limit_w, col_width));
+		}
+
+		// 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;
+		const TreeItem::Cell &c = p_item->cells[col];
+		for (int j = c.buttons.size() - 1; j >= 0; j--) {
+			const 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 - offset_button_width - 1) {
+				result.button_index = j;
+				break;
+			}
+
+			col_width -= w + theme_cache.button_margin;
 		}
-		x_check -= size.width + theme_cache.button_margin;
 	}
+
+	result.column_index = col;
+	result.column_offset = col_ofs;
+	result.column_width = col_width;
+	result.pos_x = x;
+
+	return result;
 }
 
 int Tree::get_column_at_position(const Point2 &p_pos) const {
@@ -5675,7 +5554,7 @@ int Tree::get_column_at_position(const Point2 &p_pos) const {
 
 	Point2 pos = p_pos;
 	if (is_layout_rtl()) {
-		pos.x = get_size().width - pos.x;
+		pos.x = get_size().width - pos.x - 1;
 	}
 	pos -= theme_cache.panel_style->get_offset();
 	pos.y -= _get_title_button_height();
@@ -5706,7 +5585,7 @@ int Tree::get_drop_section_at_position(const Point2 &p_pos) const {
 
 	Point2 pos = p_pos;
 	if (is_layout_rtl()) {
-		pos.x = get_size().width - pos.x;
+		pos.x = get_size().width - pos.x - 1;
 	}
 	pos -= theme_cache.panel_style->get_offset();
 	pos.y -= _get_title_button_height();
@@ -5755,7 +5634,7 @@ TreeItem *Tree::get_item_at_position(const Point2 &p_pos) const {
 
 	Point2 pos = p_pos;
 	if (is_layout_rtl()) {
-		pos.x = get_size().width - pos.x;
+		pos.x = get_size().width - pos.x - 1;
 	}
 	pos -= theme_cache.panel_style->get_offset();
 	pos.y -= _get_title_button_height();
@@ -5785,8 +5664,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;
@@ -5802,8 +5681,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;

+ 11 - 2
scene/gui/tree.h

@@ -670,9 +670,18 @@ private:
 	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;
+
+	struct FindColumnButtonResult {
+		int column_index = -1;
+		int button_index = -1;
+		int column_width = -1;
+		int column_offset = -1;
+		int pos_x = -1;
+	};
+
+	FindColumnButtonResult _find_column_and_button_at_pos(int p_x, const TreeItem *p_item, int p_x_ofs, int p_x_limit) const;
 
 	/*	float drag_speed;
 	float drag_accum;