Tooltip window bug


#1

Hi,

Adding two or more ‘\n’ at the end of the tooltip string “cuts” the tooltip window.

Example: control->setTooltip(“Hello\n\n”);


#2

This only occurs on Windows and it only takes at least one ‘\n’ at the end for the tooltip window to get cut off.

DirectWrite records a line for every new line character ‘\n’ not followed by anything. This line is empty and contains zeroes for its location and descent. If a new line character is followed by anything (including another ‘\n’) it contains the correct location and descent values.

“Hello” - 1 lines
"Hello\n" - 2 lines
"Hello\n\n" - 3 lines
"Hello\nWorld\n" - 3 lines
"Hello\nWorld\n" - 3 lines
"Hello\n\nWorld\n" - 4 lines
"Hello\nBig\nWorld\n" - 4 lines
"Hello\n\nWorld\n\n" - 5 lines

Juce uses the last line in a layout to get the height, in this case the last line has zeroes for all its values and juce thinks the size of the tooltip is 0.
There may be a couple ways this could be solved. One way, outlined below is to remove empty lines after the layout has been completed. I don’t really see a way to solve this at the DirectWrite level due to the callback nature of how line members are set.

Removing Empty Lines
The best place to correct this is probably in TextLayout::recalculateWidth() with the following block of code:

        // Remove empty lines created by DirectWrite's '\n' processing
        for (int i = lines.size() - 1; i >= 0; --i)
        {
            if (lines[i]->ascent == 0 && lines[i]->descent == 0) lines.remove(i);
            else break;
        }

The updated TextLayout::recalculateWidth() would look like this:

void TextLayout::recalculateWidth()
{
    if (lines.size() > 0)
    {
        // Remove empty lines created by DirectWrite's '\n' processing
        for (int i = lines.size() - 1; i >= 0; --i)
        {
            if (lines[i]->ascent == 0 && lines[i]->descent == 0) lines.remove(i);
            else break;
        }

        Range<float> range (lines.getFirst()->getLineBoundsX());

        int i;
        for (i = lines.size(); --i > 0;)
            range = range.getUnionWith (lines.getUnchecked(i)->getLineBoundsX());

        for (i = lines.size(); --i >= 0;)
            lines.getUnchecked(i)->lineOrigin.x -= range.getStart();

        width = range.getLength();
    }
}

#3

I had another look at this code and think the algorithm was actually a bit odd, so rather than adding a bunch of line objects, and then deleting the unused ones afterwards, I’ve changed it to only create the lines that it needs - please check out my fix and sanity-check it!


#4

Glad you found a way to make it work at the DirectWrite level.
Fix looks much better and works well over here.