commit 41d128a58a72670b6f5c3854caa47807bec16c90 Author: Campbell Barton Date: Thu Jul 6 11:13:41 2023 +1000 X diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index 8323e333139..d9e1e2889c0 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -790,12 +790,20 @@ static float vfont_descent(const VFontData *vfd) return vfd->em_height - vfont_ascent(vfd); } +/** + * Track additional information when using the cursor to select with multiple text boxes. + * This gives a more predictable result when the user moves the cursor outside the text-box. + */ typedef struct TextBoxBounds_ForCursor { /** - * Describes the minimum rectangle that contains all characters in a text-box, the values - * are scaled by `1 / #font_size`. */ - struct rctf bounds; - int last_char_index; + * Describes the minimum rectangle that contains all characters in a text-box, + * values are compatible with #TextBox. + */ + rctf bounds; + /** + * The last character in this text box or -1 when unfilled. + */ + int char_index_last; } TextBoxBounds_ForCursor; static bool vfont_to_curve(Object *ob, @@ -944,11 +952,11 @@ static bool vfont_to_curve(Object *ob, TextBoxBounds_ForCursor *tb_bounds_for_cursor = NULL; if (cursor_params != NULL) { if (cu->textoncurve == NULL && (cu->totbox > 1) && (slen > 0)) { - tb_bounds_for_cursor = MEM_calloc_arrayN( + tb_bounds_for_cursor = MEM_malloc_arrayN( cu->totbox, sizeof(TextBoxBounds_ForCursor), "TextboxBounds_Cursor"); for (curbox = 0; curbox < cu->totbox; curbox++) { TextBoxBounds_ForCursor *tb_bounds = &tb_bounds_for_cursor[curbox]; - tb_bounds->last_char_index = -1; + tb_bounds->char_index_last = -1; tb_bounds->bounds.xmin = FLT_MAX; tb_bounds->bounds.xmax = -FLT_MAX; tb_bounds->bounds.ymin = FLT_MAX; @@ -1098,7 +1106,7 @@ static bool vfont_to_curve(Object *ob, CLAMP_MIN(maxlen, lineinfo[lnr].x_min); if (tb_bounds_for_cursor != NULL) { - tb_bounds_for_cursor[curbox].last_char_index = i; + tb_bounds_for_cursor[curbox].char_index_last = i; } if ((tb_scale.h != 0.0f) && (-(yof - tb_scale.y) > (tb_scale.h - linedist) - yof_scale)) { @@ -1361,26 +1369,27 @@ static bool vfont_to_curve(Object *ob, } } if (tb_bounds_for_cursor != NULL) { + int char_beg_next = 0; for (curbox = 0; curbox < cu->totbox; curbox++) { TextBoxBounds_ForCursor *tb_bounds = &tb_bounds_for_cursor[curbox]; - if (tb_bounds->last_char_index == -1) { + if (tb_bounds->char_index_last == -1) { continue; } - const int start_char = curbox > 0 ? (tb_bounds - 1)->last_char_index + 1 : 0; - const int end_char = tb_bounds->last_char_index; + const int char_beg = char_beg_next; + const int char_end = tb_bounds->char_index_last; - struct TempLineInfo *start_line = &lineinfo[chartransdata[start_char].linenr]; - struct TempLineInfo *end_line = &lineinfo[chartransdata[end_char].linenr]; + struct TempLineInfo *line_beg = &lineinfo[chartransdata[char_beg].linenr]; + struct TempLineInfo *line_end = &lineinfo[chartransdata[char_end].linenr]; - int char_idx_offset = start_char; + int char_idx_offset = char_beg; rctf *bounds = &tb_bounds->bounds; /* In a text-box with no curves, 'yof' only decrements over lines, 'ymax' and 'ymin' * can be obtained from any character in the first and last line of the text-box. */ - bounds->ymax = chartransdata[start_char].yof; - bounds->ymin = chartransdata[end_char].yof; + bounds->ymax = chartransdata[char_beg].yof; + bounds->ymin = chartransdata[char_end].yof; - for (struct TempLineInfo *line = start_line; line <= end_line; line++) { + for (struct TempLineInfo *line = line_beg; line <= line_end; line++) { const struct CharTrans *first_char_line = &chartransdata[char_idx_offset]; const struct CharTrans *last_char_line = &chartransdata[char_idx_offset + line->char_nr]; @@ -1388,6 +1397,13 @@ static bool vfont_to_curve(Object *ob, bounds->xmax = max_ff(bounds->xmax, last_char_line->xof); char_idx_offset += line->char_nr + 1; } + /* Move the bounds into a space compatible with `cursor_location`. */ + bounds->xmin *= font_size; + bounds->xmax *= font_size; + bounds->ymin *= font_size; + bounds->ymax *= font_size; + + char_beg_next = tb_bounds->char_index_last + 1; } } @@ -1833,8 +1849,8 @@ static bool vfont_to_curve(Object *ob, } else { /* Find the first box closest to `cursor_location`. */ - int start = 0; - int end = slen; + int char_beg = 0; + int char_end = slen; if (tb_bounds_for_cursor != NULL) { /* Search for the closest box. */ @@ -1842,21 +1858,17 @@ static bool vfont_to_curve(Object *ob, float closest_dist_sq = FLT_MAX; for (curbox = 0; curbox < cu->totbox; curbox++) { const TextBoxBounds_ForCursor *tb_bounds = &tb_bounds_for_cursor[curbox]; - if (tb_bounds->last_char_index == -1) { + if (tb_bounds->char_index_last == -1) { continue; } /* The closest point in the box to the `cursor_location` * by clamping it to the bounding box. */ - const float closest_point[2] = { - clamp_f(cursor_location[0], - tb_bounds->bounds.xmin * font_size, - tb_bounds->bounds.xmax * font_size), - clamp_f(cursor_location[1], - tb_bounds->bounds.ymin * font_size, - tb_bounds->bounds.ymax * font_size), + const float cursor_location_clamped[2] = { + clamp_f(cursor_location[0], tb_bounds->bounds.xmin, tb_bounds->bounds.xmax), + clamp_f(cursor_location[1], tb_bounds->bounds.ymin, tb_bounds->bounds.ymax), }; - const float test_dist_sq = len_squared_v2v2(cursor_location, closest_point); + const float test_dist_sq = len_squared_v2v2(cursor_location, cursor_location_clamped); if (test_dist_sq < closest_dist_sq) { closest_dist_sq = test_dist_sq; closest_box = curbox; @@ -1864,31 +1876,31 @@ static bool vfont_to_curve(Object *ob, } if (closest_box != -1) { if (closest_box != 0) { - start = tb_bounds_for_cursor[closest_box - 1].last_char_index + 1; + char_beg = tb_bounds_for_cursor[closest_box - 1].char_index_last + 1; } - end = tb_bounds_for_cursor[closest_box].last_char_index; + char_end = tb_bounds_for_cursor[closest_box].char_index_last; } MEM_freeN(tb_bounds_for_cursor); tb_bounds_for_cursor = NULL; /* Safety only. */ } const float interline_offset = ((linedist - 0.5f) / 2.0f) * font_size; /* Loop until find the line where `cursor_location` is over. */ - for (i = start; i <= end; i++) { + for (i = char_beg; i <= char_end; i++) { if (cursor_location[1] >= ((chartransdata[i].yof * font_size) - interline_offset)) { break; } } - i = end < i ? end : i; - float ch_yof = chartransdata[i].yof; + i = min_ii(i, char_end); + float char_yof = chartransdata[i].yof; /* Loop back until find the first character of the line, this because `cursor_location` can * be positioned further below the text, so #i can be the last character of the last line. */ - for (; i >= start + 1 && chartransdata[i - 1].yof == ch_yof; i--) { + for (; i >= char_beg + 1 && chartransdata[i - 1].yof == char_yof; i--) { } /* Loop until find the first character to the right of `cursor_location` * (using the character midpoint on the x-axis as a reference). */ - for (; i <= end && ch_yof == chartransdata[i].yof; i++) { + for (; i <= char_end && char_yof == chartransdata[i].yof; i++) { info = &custrinfo[i]; ascii = info->flag & CU_CHINFO_SMALLCAPS_CHECK ? towupper(mem[i]) : mem[i]; che = find_vfont_char(vfd, ascii); @@ -1898,12 +1910,13 @@ static bool vfont_to_curve(Object *ob, break; } } - i = i <= end ? i : end; + i = min_ii(i, char_end); /* If there is no character to the right of the cursor we are on the next line, go back to * the last character of the previous line. */ - i = i > start && chartransdata[i].yof != ch_yof ? i - 1 : i; - + if (i > char_beg && chartransdata[i].yof != char_yof) { + i -= 1; + } cursor_params->r_string_offset = i; } /* Must be cleared & freed. */