From d2b112d1c15d8f833d9028590ee26c8e5a2c8365 Mon Sep 17 00:00:00 2001 From: "chunmao.guo" Date: Thu, 3 Nov 2022 11:56:49 +0800 Subject: [PATCH] ENH: Label Wrap Change-Id: I7492f058f731a68e441ab9b78693872364fc00ab --- src/slic3r/GUI/Widgets/Label.cpp | 124 +++++++++++++++++++++++++++++++ src/slic3r/GUI/Widgets/Label.hpp | 2 + 2 files changed, 126 insertions(+) diff --git a/src/slic3r/GUI/Widgets/Label.cpp b/src/slic3r/GUI/Widgets/Label.cpp index 23279ff95..9fca2bec3 100644 --- a/src/slic3r/GUI/Widgets/Label.cpp +++ b/src/slic3r/GUI/Widgets/Label.cpp @@ -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); +} diff --git a/src/slic3r/GUI/Widgets/Label.hpp b/src/slic3r/GUI/Widgets/Label.hpp index 65f10d009..634f73a75 100644 --- a/src/slic3r/GUI/Widgets/Label.hpp +++ b/src/slic3r/GUI/Widgets/Label.hpp @@ -17,6 +17,8 @@ public: void SetWindowStyleFlag(long style) override; + void Wrap(int width); + private: wxFont font; wxColour color;