相关问题:Getting a string index based on a pixel offset
我知道这很接近这个问题,但这不是问如何直接做,而是询问如何最好地伪造它。
我正在为Windows Forms实现我自己的文本框(因为RichTextBox很糟糕),我正在尝试找到最好的方法,给定在屏幕上绘制的字符串,计算鼠标结束的字符。问题是字符可以是可变宽度。
我想出了两种可能性:
每次鼠标移动时Graphics.MeasureCharacterRange
以鼠标移动的方式以二进制搜索方式移动<(如顶部链接的问题所示)
保留每行每个字符的偏移列表。
(1)表现不佳,
(2)将是内存效率低的加上使键入一个字符成为一个O(n)操作(因为你必须调整它后面的每个字符的偏移量) plus 不能完全做到,因为Graphics.MeasureCharacterRange
不准确(它为一个字符返回一个值,为另一个字符返回另一个值,并且在一个字符串中将两个值完全不同的值[不等于前面添加的两个值]]。例如W
宽16像素,f
宽5像素,但Wf
宽20像素。这些数字来自实际测试。)。
所以我正在寻找一个更好的策略来做到这一点,最好是需要最小空间和O(1)计算复杂度的策略(尽管我很乐意用一点点内存效率来提高速度效率)。
答案 0 :(得分:1)
我认为你不必做O(1)。 O(1)假设每个附加字符对所有先前字符都有影响,而不是。充其量我会看到每个单词的O(1)应该是疯狂的。听起来你需要的是一种存储方式; 1每个单词的位置,每个单词2个,单词中每个字母的宽度为3个。这将显着减少存储并提高查找速度。也许是这样的:
IEnumerable<TextLocation> TextLocations = ...;
internal class TextLocation
{
public RectF BoundingBox { get; set; } //this is relative to the textbox
public TextWord TextWord { get; set; }
}
internal class TextWord
{
public string Text { get; set; }
public IEnumerable<LetterInfo> Letters { get; set; }
}
internal class LetterInfo
{
public char Letter { get; set; }
public float left { get; set; } //these would be relative to the bounding box
public float right { get; set; } //not to the textbox
}
然后你可能会做类似
的事情var tl = TextLocations.FirstOrDefault(x => x.BoundingBox.Left < Mouse.X
&& x.BoundingBox.Right > Mouse.X
&& x.BoundingBox.Top < Mouse.Y
&& x.BoundingBox.Bottom > Mouse.Y)
if (tl != null)
{
//tl.TextWord.Text is the Word ("The", "Lazy", "Dog"...)
var letter = tl.TextWord.Letters
.FirstOrDefault(x => Mouse.x - tl.BoundingBox.left > x.left
Mouse.x - tl.BoundingBox.left < x.right);
if (letter != null)
{
// you get the idea
}
}