From 52f950b28d0e387cb806c89060a0cf9d332739e9 Mon Sep 17 00:00:00 2001 From: hz Date: Thu, 9 Jul 2026 21:44:59 -0500 Subject: [PATCH] Map highlights to selected text near cursor --- scripts/ui/question_player.gd | 45 ++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/scripts/ui/question_player.gd b/scripts/ui/question_player.gd index 5fe9874..bf58636 100644 --- a/scripts/ui/question_player.gd +++ b/scripts/ui/question_player.gd @@ -12,6 +12,8 @@ var question_highlights := {} var stem_plain_text := "" var highlight_mode_enabled := false var highlight_drag_started_in_stem := false +var highlight_drag_start_position := Vector2.ZERO +var highlight_drag_end_position := Vector2.ZERO var progress_label: Label var timer_label: Label @@ -160,8 +162,10 @@ func _input(event: InputEvent) -> void: if mouse_event.pressed: highlight_drag_started_in_stem = stem_label.get_global_rect().has_point(mouse_event.global_position) + highlight_drag_start_position = mouse_event.global_position elif highlight_drag_started_in_stem: highlight_drag_started_in_stem = false + highlight_drag_end_position = mouse_event.global_position call_deferred("_commit_current_text_selection") @@ -311,16 +315,10 @@ func _commit_current_text_selection() -> void: if selected_text == "": return - var selection_start := stem_label.get_selection_from() - var selection_end := stem_label.get_selection_to() - if selection_start == selection_end: + var selected_range := _find_selected_text_range_near_drag(selected_text) + if selected_range.is_empty(): return - if selection_start > selection_end: - var previous_start := selection_start - selection_start = selection_end - selection_end = previous_start - - _highlight_range(selection_start, selection_end) + _highlight_range(int(selected_range["start"]), int(selected_range["end"])) func _highlight_range(start: int, end: int) -> void: @@ -375,6 +373,35 @@ func _escape_bbcode(value: String) -> String: return value.replace("[", "[lb]").replace("]", "[rb]") +func _find_selected_text_range_near_drag(selected_text: String) -> Dictionary: + var matches: Array[Dictionary] = [] + var search_from := 0 + while search_from < stem_plain_text.length(): + var start := stem_plain_text.find(selected_text, search_from) + if start == -1: + break + matches.append({"start": start, "end": start + selected_text.length()}) + search_from = start + max(1, selected_text.length()) + + if matches.is_empty(): + return {} + if matches.size() == 1: + return matches[0] + + var local_midpoint := stem_label.get_global_transform().affine_inverse() * ((highlight_drag_start_position + highlight_drag_end_position) * 0.5) + var line: int = stem_label.get_line_at_pos(int(local_midpoint.y)) + var clicked_character: int = stem_label.get_character_line(line) + var best_match: Dictionary = matches[0] + var best_distance: int = abs(int(best_match["start"]) - clicked_character) + for index in range(1, matches.size()): + var candidate: Dictionary = matches[index] + var distance: int = abs(int(candidate["start"]) - clicked_character) + if distance < best_distance: + best_distance = distance + best_match = candidate + return best_match + + func _remove_overlapping_highlights(highlights: Array, start: int, end: int) -> bool: var removed := false for index in range(highlights.size() - 1, -1, -1):