traverse the layout to locate the nearest character to a display position
| 394 | |
| 395 | // traverse the layout to locate the nearest character to a display position |
| 396 | static int stb_text_locate_coord(STB_TEXTEDIT_STRING* str, float x, float y) |
| 397 | { |
| 398 | StbTexteditRow r; |
| 399 | int n = STB_TEXTEDIT_STRINGLEN(str); |
| 400 | float base_y = 0, prev_x; |
| 401 | int i = 0, k; |
| 402 | |
| 403 | r.x0 = r.x1 = 0; |
| 404 | r.ymin = r.ymax = 0; |
| 405 | r.num_chars = 0; |
| 406 | |
| 407 | // search rows to find one that straddles 'y' |
| 408 | while (i < n) { |
| 409 | STB_TEXTEDIT_LAYOUTROW(&r, str, i); |
| 410 | if (r.num_chars <= 0) |
| 411 | return n; |
| 412 | |
| 413 | if (i == 0 && y < base_y + r.ymin) |
| 414 | return 0; |
| 415 | |
| 416 | if (y < base_y + r.ymax) |
| 417 | break; |
| 418 | |
| 419 | i += r.num_chars; |
| 420 | base_y += r.baseline_y_delta; |
| 421 | } |
| 422 | |
| 423 | // below all text, return 'after' last character |
| 424 | if (i >= n) |
| 425 | return n; |
| 426 | |
| 427 | // check if it's before the beginning of the line |
| 428 | if (x < r.x0) |
| 429 | return i; |
| 430 | |
| 431 | // check if it's before the end of the line |
| 432 | if (x < r.x1) { |
| 433 | // search characters in row for one that straddles 'x' |
| 434 | prev_x = r.x0; |
| 435 | for (k = 0; k < r.num_chars; ++k) { |
| 436 | float w = STB_TEXTEDIT_GETWIDTH(str, i, k); |
| 437 | if (x < prev_x + w) { |
| 438 | if (x < prev_x + w / 2) |
| 439 | return k + i; |
| 440 | else |
| 441 | return k + i + 1; |
| 442 | } |
| 443 | prev_x += w; |
| 444 | } |
| 445 | // shouldn't happen, but if it does, fall through to end-of-line case |
| 446 | } |
| 447 | |
| 448 | // if the last character is a newline, return that. otherwise return 'after' the last character |
| 449 | if (STB_TEXTEDIT_GETCHAR(str, i + r.num_chars - 1) == STB_TEXTEDIT_NEWLINE) |
| 450 | return i + r.num_chars - 1; |
| 451 | else |
| 452 | return i + r.num_chars; |
| 453 | } |
no test coverage detected