Merge branch 'ellipsize-fixes'

Fix ellipsization of strings containing mnemonics and TABs and also
improve handling of trailing spaces (by ignoring them).

See https://github.com/wxWidgets/wxWidgets/pull/1649
This commit is contained in:
Vadim Zeitlin
2019-11-16 00:51:56 +01:00
2 changed files with 53 additions and 29 deletions

View File

@@ -293,33 +293,62 @@ struct EllipsizeCalculator
m_maxFinalWidthPx(maxFinalWidthPx),
m_replacementWidthPx(replacementWidthPx)
{
m_isOk = dc.GetPartialTextExtents(s, m_charOffsetsPx);
wxASSERT( m_charOffsetsPx.GetCount() == s.length() );
size_t expectedOffsetsCount = s.length();
// Where ampersands are used as mnemonic indicator they should not
// affect the overall width of the string and must be removed from the
// measurement. Nonetheless, we need to keep them in the string and
// have a corresponding entry in m_charOffsetsPx.
if ( flags & wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS )
{
// The ampersand itself shouldn't count for the width calculation
// as it won't be displayed: either it's an ampersand before some
// other character in which case it indicates a mnemonic, or it's
// an escaped ampersand, in which case only the second one of the
// pair of ampersands will be displayed. But we can't just remove
// the ampersand as we still need it in the final label, in order
// not to lose the mnemonics. Hence we use this hack, and pretend
// that ampersands simply have zero width. Of course, it could be
// not completely precise, but this is the best we can do without
// completely changing this code structure.
// Create a copy of the string with the ampersands removed to get
// the correct widths.
const wxString cpy = wxControl::RemoveMnemonics(s);
m_isOk = dc.GetPartialTextExtents(cpy, m_charOffsetsPx);
// Iterate through the original string inserting a cumulative width
// value for each ampersand that is the same as the following
// character's cumulative width value. Except this is only done
// for the first ampersand in a pair (see RemoveMnemonics).
size_t n = 0;
int delta = 0;
bool lastWasMnemonic = false;
for ( wxString::const_iterator it = s.begin();
it != s.end();
++it, ++n )
{
if ( *it == '&' )
delta += dc.GetTextExtent(wxS('&')).GetWidth();
m_charOffsetsPx[n] -= delta;
if ( *it == '&' && !lastWasMnemonic )
{
if ( (it + 1) != s.end() )
{
int w = m_charOffsetsPx[n];
m_charOffsetsPx.Insert(w, n);
lastWasMnemonic = true;
}
else // Last character is an ampersand.
{
// This ampersand is removed by RemoveMnemonics() and
// won't be displayed when this string is drawn
// neither, so we intentionally don't use it for our
// calculations neither -- just account for this in the
// assert below.
expectedOffsetsCount--;
}
}
else // Not an ampersand used to introduce a mnemonic.
{
lastWasMnemonic = false;
}
}
}
else
{
m_isOk = dc.GetPartialTextExtents(s, m_charOffsetsPx);
}
// Either way, we should end up with the same number of offsets as
// characters in the original string.
wxASSERT( m_charOffsetsPx.GetCount() == expectedOffsetsCount );
}
bool IsOk() const { return m_isOk; }
@@ -567,6 +596,7 @@ wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc,
{
if ( pc == label.end() || *pc == wxS('\n') )
{
curLine.Trim();
curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth,
replacementWidth, flags);

View File

@@ -23,14 +23,7 @@
// test class
// ----------------------------------------------------------------------------
class EllipsizationTestCase
{
public:
EllipsizationTestCase() { }
};
TEST_CASE_METHOD(EllipsizationTestCase, "Ellipsization::NormalCase", "[ellipsization]")
TEST_CASE("Ellipsization::NormalCase", "[ellipsization]")
{
wxMemoryDC dc;
@@ -101,8 +94,9 @@ TEST_CASE_METHOD(EllipsizationTestCase, "Ellipsization::NormalCase", "[ellipsiza
WX_ASSERT_MESSAGE
(
(
"Test #(%u,%u.%u): \"%s\" -> \"%s\"; width=%dpx > %dpx",
"Test #(%u,%u.%u): %s\n\"%s\" -> \"%s\"; width=%dpx > %dpx",
s, f, m,
dc.GetFont().GetNativeFontInfoUserDesc(),
str,
ret,
width,
@@ -117,7 +111,7 @@ TEST_CASE_METHOD(EllipsizationTestCase, "Ellipsization::NormalCase", "[ellipsiza
}
TEST_CASE_METHOD(EllipsizationTestCase, "Ellipsization::EnoughSpace", "[ellipsization]")
TEST_CASE("Ellipsization::EnoughSpace", "[ellipsization]")
{
// No ellipsization should occur if there's plenty of space.
@@ -132,7 +126,7 @@ TEST_CASE_METHOD(EllipsizationTestCase, "Ellipsization::EnoughSpace", "[ellipsiz
}
TEST_CASE_METHOD(EllipsizationTestCase, "Ellipsization::VeryLittleSpace", "[ellipsization]")
TEST_CASE("Ellipsization::VeryLittleSpace", "[ellipsization]")
{
// If there's not enough space, the shortened label should still contain "..." and one character
@@ -147,7 +141,7 @@ TEST_CASE_METHOD(EllipsizationTestCase, "Ellipsization::VeryLittleSpace", "[elli
}
TEST_CASE_METHOD(EllipsizationTestCase, "Ellipsization::HasThreeDots", "[ellipsization]")
TEST_CASE("Ellipsization::HasThreeDots", "[ellipsization]")
{
wxMemoryDC dc;