Changeset 26915

Timestamp:
Jun 2, 2022, 2:56:53 PM (2 years ago)
Author:
wraitii
Message:

Fix text alignment handling of spaces around wrapping.

Follows rP26889.

There is an issue with text-wrapping and word separators (aka spaces).
Because 0 A.D. collates the space after a word to the same TextCall, we occasionally need to ignore it when considering line-wrapping, because we don't want empty spaces on the right-side of right-aligned text.
However, the logic to handle this is currently broken and inconsistent.

The method introduced here uses the SFeedback structure to properly report it and generalises the checks.

Note that multiples spaces are not collapsed in 0 A.D., and for consistency the word-separator-collapsing behaviour is ignored.

Comments by: phosit, vlasdislavbelov

Fixes #6551

Differential Revision: https://code.wildfiregames.com/D4662

Location:
ps/trunk
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • ps/trunk/binaries/data/mods/_test.minimal/fonts/console.fnt

    r7518 r26915  
    1 100
     110
    22256 256
     3
    34606
    4 20
     515
     612
    5732 0 256 0 0 0 0 5
    6833 250 154 3 11 1 11 4
  • ps/trunk/source/gui/CGUIText.cpp

    r26889 r26915  
    8080                            //  in order to avoid duplicate processing.
    8181
    82     // Go through string word by word
     82    // The calculated width of each word includes the space between the current
     83    // word and the next. When we're wrapping, we need subtract the width of the
     84    // space after the last word on the line before the wrap.
     85    CFontMetrics currentFont(font);
     86    float spaceWidth = currentFont.GetCharacterWidth(L' ');
     87
     88    // Go through string word by word.
     89    // a word is defined as [start, end[ in string.m_Words so we skip the last item.
    8390    for (int i = 0; i < static_cast<int>(string.m_Words.size()) - 1; ++i)
    8491    {
     
    103110        prelimLineHeight = std::max(prelimLineHeight, feedback.m_Size.Height);
    104111
     112
     113
    105114        // If width is 0, then there's no word-wrapping, disable NewLine.
    106         if ((width != 0 && from != i && (lineWidth + 2 * bufferZone > width || feedback.m_NewLine)) || i == static_cast<int>(string.m_Words.size()) - 2)
     115        if ((width != 0 && from != i && (lineWidth + 2 * bufferZone > width || feedback.m_NewLine)) || i == static_cast<int>(string.m_Words.size()) - 2)
    107116        {
    108117            if (ProcessLine(pGUI, string, font, pObject, images, align, prelimLineHeight, width, bufferZone, firstLine, y, i, from))
     
    174183    CSize2D& lineSize) const
    175184{
     185
     186
     187
     188
     189
     190
     191
     192
    176193    float x = widthRangeFrom;
    177194    for (int j = tempFrom; j <= i; ++j)
     
    188205        x += feedback2.m_Size.Width;
    189206
    190         if (width != 0 && x > widthRangeTo && j != tempFrom && !feedback2.m_NewLine)
    191         {
    192             // The calculated width of each word includes the space between the current
    193             // word and the next. When we're wrapping, we need subtract the width of the
    194             // space after the last word on the line before the wrap.
    195             CFontMetrics currentFont(font);
    196             lineSize.Width -= currentFont.GetCharacterWidth(*L" ");
     207        if (width != 0 && x - spaceCorrection > widthRangeTo && j != tempFrom && !feedback2.m_NewLine)
    197208            break;
    198         }
     209
     210        // Update after the line-break detection, because otherwise spaceCorrection above
     211        // will refer to the wrapped word and not the last-word-before-the-line-break.
     212        spaceCorrection = feedback2.m_EndsWithSpace ? spaceWidth : 0.f;
    199213
    200214        // Let lineSize.cy be the maximum m_Height we encounter.
     
    210224        lineSize.Width += feedback2.m_Size.Width;
    211225    }
     226
     227
    212228}
    213229
     
    365381        }
    366382
    367         // Append X value.
    368383        x += feedback2.m_Size.Width;
    369384
    370         // The first word overrides the width limit, what we
    371         //  do, in those cases, are just drawing that word even
    372         //  though it'll extend the object.
    373385        if (width != 0) // only if word-wrapping is applicable
    374386        {
     387
     388
     389
     390
     391
     392
    375393            if (feedback2.m_NewLine)
    376394            {
     
    393411                break;
    394412            }
    395             else if (x > widthRangeTo && j == tempFrom)
     413            else if (x > widthRangeTo && j == tempFrom)
    396414            {
     415
     416
     417
     418
    397419                from = j+1;
    398                 // do not break, since we want it to be added to m_TextCalls
     420                // To avoid doing redundant computations, set up j to exit the loop right away.
     421                j = i + 1;
    399422            }
    400             else if (x > widthRangeTo)
     423            else if (x > widthRangeTo)
    401424            {
    402425                from = j;
  • ps/trunk/source/gui/SettingTypes/CGUIString.cpp

    r26523 r26915  
    230230            if (!TextCall.m_String.empty() && TextCall.m_String[0] == '\n')
    231231                Feedback.m_NewLine = true;
     232
     233
     234
     235
     236
     237
     238
     239
     240
     241
     242
    232243
    233244            // Add text-chunk
  • ps/trunk/source/gui/SettingTypes/CGUIString.h

    r25353 r26915  
    1 /* Copyright (C) 2021 Wildfire Games.
     1/* Copyright (C) 202 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
     
    157157         */
    158158        bool m_NewLine;
     159
     160
     161
     162
     163
    159164    };
    160165
Note: See TracChangeset for help on using the changeset viewer.