Preserve mnemonics in ellipsized labels

Ellipsization code was completely broken when used with the usual
control label strings containing mnemonics: it simply stripped the
mnemonics completely, losing them even if the label wasn't actually
ellipsized, and turned "&&" into a mnemonic. I.e. "&Plug && play"
appeared without underlined "P" but with underlined space before "play"
before.

Fix this by pretending that all ampersands in the string to be
ellipsized have zero width. This is not precise, as the result of
GetPartialTextExtents() for a string with the ampersands is not exactly
the same as the sum of its result for the string without the ampersands
and the width of the ampersands themselves, but it should be pretty
close and unlikely to result in any problems in practice for the
controls labels.

At the very least this fixes the completely wrong behaviour of the
controls on the "Static" page of the widgets sample, where ellipsization
is enabled by default and setting the label text with mnemonics didn't
work at all.
This commit is contained in:
Vadim Zeitlin
2019-06-20 01:48:33 +02:00
parent 30726437c0
commit 63a40a09b2

View File

@@ -282,7 +282,8 @@ namespace
struct EllipsizeCalculator struct EllipsizeCalculator
{ {
EllipsizeCalculator(const wxString& s, const wxDC& dc, EllipsizeCalculator(const wxString& s, const wxDC& dc,
int maxFinalWidthPx, int replacementWidthPx) int maxFinalWidthPx, int replacementWidthPx,
int flags)
: :
m_initialCharToRemove(0), m_initialCharToRemove(0),
m_nCharsToRemove(0), m_nCharsToRemove(0),
@@ -294,6 +295,31 @@ struct EllipsizeCalculator
{ {
m_isOk = dc.GetPartialTextExtents(s, m_charOffsetsPx); m_isOk = dc.GetPartialTextExtents(s, m_charOffsetsPx);
wxASSERT( m_charOffsetsPx.GetCount() == s.length() ); wxASSERT( m_charOffsetsPx.GetCount() == s.length() );
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.
size_t n = 0;
int delta = 0;
for ( wxString::const_iterator it = s.begin();
it != s.end();
++it, ++n )
{
if ( *it == '&' )
delta += dc.GetTextExtent(wxS('&')).GetWidth();
m_charOffsetsPx[n] -= delta;
}
}
} }
bool IsOk() const { return m_isOk; } bool IsOk() const { return m_isOk; }
@@ -405,7 +431,7 @@ struct EllipsizeCalculator
wxString DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc, wxString DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc,
wxEllipsizeMode mode, int maxFinalWidthPx, wxEllipsizeMode mode, int maxFinalWidthPx,
int replacementWidthPx) int replacementWidthPx, int flags)
{ {
wxASSERT_MSG(replacementWidthPx > 0, "Invalid parameters"); wxASSERT_MSG(replacementWidthPx > 0, "Invalid parameters");
wxASSERT_LEVEL_2_MSG(!curLine.Contains('\n'), wxASSERT_LEVEL_2_MSG(!curLine.Contains('\n'),
@@ -413,9 +439,6 @@ wxString DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc,
wxASSERT_MSG( mode != wxELLIPSIZE_NONE, "shouldn't be called at all then" ); wxASSERT_MSG( mode != wxELLIPSIZE_NONE, "shouldn't be called at all then" );
// NOTE: this function assumes that any mnemonic/tab character has already
// been handled if it was necessary to handle them (see Ellipsize())
if (maxFinalWidthPx <= 0) if (maxFinalWidthPx <= 0)
return wxEmptyString; return wxEmptyString;
@@ -423,7 +446,7 @@ wxString DoEllipsizeSingleLine(const wxString& curLine, const wxDC& dc,
if (len <= 1 ) if (len <= 1 )
return curLine; return curLine;
EllipsizeCalculator calc(curLine, dc, maxFinalWidthPx, replacementWidthPx); EllipsizeCalculator calc(curLine, dc, maxFinalWidthPx, replacementWidthPx, flags);
if ( !calc.IsOk() ) if ( !calc.IsOk() )
return curLine; return curLine;
@@ -545,7 +568,7 @@ wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc,
if ( pc == label.end() || *pc == wxS('\n') ) if ( pc == label.end() || *pc == wxS('\n') )
{ {
curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth, curLine = DoEllipsizeSingleLine(curLine, dc, mode, maxFinalWidth,
replacementWidth); replacementWidth, flags);
// add this (ellipsized) row to the rest of the label // add this (ellipsized) row to the rest of the label
ret << curLine; ret << curLine;
@@ -555,15 +578,6 @@ wxString wxControlBase::Ellipsize(const wxString& label, const wxDC& dc,
ret << *pc; ret << *pc;
curLine.clear(); curLine.clear();
} }
// we need to remove mnemonics from the label for correct calculations
else if ( *pc == wxS('&') && (flags & wxELLIPSIZE_FLAGS_PROCESS_MNEMONICS) )
{
// pc+1 is safe: at worst we'll be at end()
wxString::const_iterator next = pc + 1;
if ( next != label.end() && *next == wxS('&') )
curLine += wxS('&'); // && becomes &
//else: remove this ampersand
}
// we need also to expand tabs to properly calc their size // we need also to expand tabs to properly calc their size
else if ( *pc == wxS('\t') && (flags & wxELLIPSIZE_FLAGS_EXPAND_TABS) ) else if ( *pc == wxS('\t') && (flags & wxELLIPSIZE_FLAGS_EXPAND_TABS) )
{ {