ENH: Label Wrap

Change-Id: I7492f058f731a68e441ab9b78693872364fc00ab
This commit is contained in:
chunmao.guo 2022-11-03 11:56:49 +08:00 committed by Lane.Wei
parent 4d4fcc1780
commit d2b112d1c1
2 changed files with 126 additions and 0 deletions

View file

@ -61,6 +61,124 @@ void Label::initSysFont()
Body_9 = Label::sysFont(9, false);
}
class WXDLLIMPEXP_CORE wxTextWrapper2
{
public:
wxTextWrapper2() { m_eol = false; }
// win is used for getting the font, text is the text to wrap, width is the
// max line width or -1 to disable wrapping
void Wrap(wxWindow *win, const wxString &text, int widthMax)
{
const wxClientDC dc(win);
const wxArrayString ls = wxSplit(text, '\n', '\0');
for (wxArrayString::const_iterator i = ls.begin(); i != ls.end(); ++i) {
wxString line = *i;
if (i != ls.begin()) {
// Do this even if the line is empty, except if it's the first one.
OnNewLine();
}
// Is this a special case when wrapping is disabled?
if (widthMax < 0) {
DoOutputLine(line);
continue;
}
for (bool newLine = false; !line.empty(); newLine = true) {
if (newLine) OnNewLine();
wxArrayInt widths;
dc.GetPartialTextExtents(line, widths);
const size_t posEnd = std::lower_bound(widths.begin(), widths.end(), widthMax) - widths.begin();
// Does the entire remaining line fit?
if (posEnd == line.length()) {
DoOutputLine(line);
break;
}
// Find the last word to chop off.
size_t lastSpace = posEnd;
while (lastSpace != size_t(-1)) {
auto c = line[lastSpace];
if (c == ' ' || c > 0x4E00)
break;
--lastSpace;
}
if (lastSpace == size_t(-1)) {
// No spaces, so can't wrap.
DoOutputLine(line);
break;
}
// Output the part that fits.
DoOutputLine(line.substr(0, lastSpace));
// And redo the layout with the rest.
if (line[lastSpace] == ' ') ++lastSpace;
line = line.substr(lastSpace);
}
}
}
// we don't need it, but just to avoid compiler warnings
virtual ~wxTextWrapper2() {}
protected:
// line may be empty
virtual void OnOutputLine(const wxString &line) = 0;
// called at the start of every new line (except the very first one)
virtual void OnNewLine() {}
private:
// call OnOutputLine() and set m_eol to true
void DoOutputLine(const wxString &line)
{
OnOutputLine(line);
m_eol = true;
}
// this function is a destructive inspector: when it returns true it also
// resets the flag to false so calling it again wouldn't return true any
// more
bool IsStartOfNewLine()
{
if (!m_eol) return false;
m_eol = false;
return true;
}
bool m_eol;
};
class wxLabelWrapper2 : public wxTextWrapper2
{
public:
void WrapLabel(wxWindow *text, int widthMax)
{
m_text.clear();
Wrap(text, text->GetLabel(), widthMax);
text->SetLabel(m_text);
}
protected:
virtual void OnOutputLine(const wxString &line) wxOVERRIDE { m_text += line; }
virtual void OnNewLine() wxOVERRIDE { m_text += wxT('\n'); }
private:
wxString m_text;
};
wxSize Label::split_lines(wxDC &dc, int width, const wxString &text, wxString &multiline_text)
{
multiline_text = text;
@ -136,3 +254,9 @@ void Label::SetWindowStyleFlag(long style)
}
Refresh();
}
void Label::Wrap(int width)
{
wxLabelWrapper2 wrapper;
wrapper.WrapLabel(this, width);
}