1

相关问题:获取基于像素偏移的字符串索引

我知道这接近那个问题,但这不是问如何直接做,而是问如何最好地伪造它。


我正在为 Windows 窗体实现我自己的文本框(因为 RichTextBox 很烂),并且我试图找到最好的方法,给定屏幕上绘制的字符串,计算鼠标所在的字符。问题是字符可以是可变宽度的。

我想出了两种可能:

  1. Graphics.MeasureCharacterRange 每次鼠标在鼠标所在的行上以二进制搜索方式移动时执行(如顶部链接的问题中所建议)

  2. 保留每行每个字符的偏移量列表。

(1) 表现不佳,且

(2)将是内存效率低下加上输入一个字符成为一个O(n)操作(因为你必须调整它之后的每个字符的偏移量)加上不可能精确地做,因为Graphics.MeasureCharacterRange不精确(它返回一个值一个字符,另一个字符的另一个值,以及一个完全不同的值[不等于前面的两个值加在一起],它们都在一个字符串中。例如W,16 像素宽, 5 像素宽f,但是Wf20 像素宽。这些数字来自实际测试。)。

所以我正在寻找一种更好的策略来做到这一点,最好是需要最小空间和 O(1) 计算复杂度的策略(尽管我很乐意用一点内存效率来换取速度效率)。

4

1 回答 1

1

I don't think you have to do O(1). O(1) is assuming that every additional character has an affect on ALL previous characters, which it would not. At best I would see an O(1) for each word which should be crazy fast. It sounds like what you need is a way to store; 1 the location of each word, 2 each unique word, and 3 the width of each letter in the word. This would significantly reduce the storage and increase look up speed. Maybe something like:

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
}

Then you might be able to do something like

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
    }                              
}
于 2011-09-09T02:29:35.463 回答