нашёл некоторый… скажем так, недочёт в TextViews. как мы знаем, оригинальный виндовый BBCB в принципе забивает большой и толстый на кернинг. лин-версия использует панго (или, в случае LC, свою рисовалку на основе FreeType), и кернинг уважает. это
почти работает, за одним интересным исключением.
штука такая, что TextSetters.Reader разбивает текст не только по кусочкам с разными атрибутами, но и на некоторых специальных символах типа переноса и минуса. он это делает для реализации wrapping. также максимальный размер возвращаемого кусочка — 64. и вот тут возникает противоречие между `TextViews.DrawLine()` и `TextViews.GetThisLocation()`. `DrawLine()` кэширует строки кусками по 128, и вдобавок не разбивает их по границам спецсимволов. а `GetThisLocation()` такого не делает. из-за этого в некоторых случаях вычисленая позиция каретки может не соответствовать реальной позиции глифа на экране.
то есть: `GetThisLocation()` двигается ровно по кусочкам, возвращённым Reader, через `StringWidth()`. а теперь представим такой случай: знак минуса имеет ненулевой кернинг с некоторыми буквами (так, например, в вердане: "A-" — тут минус смещают немного влево. `DrawLine()` накопит строку "A-" и нарисует минус с правильным кернингом. а `GetThisLocation()` вычислит сумму длин строк "A" и "-" отдельно, и каретка уедет немного дальше вправо, чем надо.
такая штука очень малозаметна, но тем не менее она существует. если в строке накопится много минусов с кернингом — каретка довольно заметно визуально сместится.
вариантов решения я тут вижу два: или учить `GetThisLocation()` кэшировать текст точно так же, как делает `DrawLine()`, или выкинуть из `DrawLine()` кэширование совсем. второй вариант мне кажется более правильным: просто отключить кэширование в `DrawLine()`. техника сейчас достаточно быстрая, чтобы не имело смысла собирать там кусочки (тем более что частично кусочки уже собирает Reader).
Код:
PROCEDURE CacheString (x, y: INTEGER; c: INTEGER; IN s: ARRAY OF CHAR; f: Fonts.Font);
VAR i, j, len: INTEGER;
BEGIN
len := LEN(s$);
IF FALSE THEN (* old code, unused *)
IF (cache.len + len >= cacheLen) OR (cache.y # y) OR
(cache.color # c) OR (cache.font # f)
THEN
FlushCaches
END;
ASSERT(cache.len + len < cacheLen, 100);
IF cache.len = 0 THEN
cache.x := x; cache.y := y;
cache.color := c; cache.font := f
END;
i := 0; j := cache.len;
WHILE i < len DO cache.buf[j] := s[i]; INC(i); INC(j) END;
cache.len := j
ELSE
FlushCaches;
ASSERT(cache.len = 0);
cache.x := x; cache.y := y;
cache.color := c; cache.font := f;
cache.buf := s$; cache.len := len
END
END CacheString;
старый код не удаляем — пусть будет, мало ли. просто вместо этого не склеиваем строки, даже если возможно. это не ломает никакой логики, и решает все потенциальные проблемы с кернингом.
в LC я просто удалил всю логику кэширования из `DrawLine()`. реально она почти ничего не ускоряет, только зря усложняет код.