From 0443cd4eed07c139f5d079cc7a96f986a56b2425 Mon Sep 17 00:00:00 2001 From: thankyouverycool <66646555+thankyouverycool@users.noreply.github.com> Date: Mon, 1 Mar 2021 14:52:04 -0500 Subject: [PATCH] LibGUI: Add word wrapping to Labels Adds basic word wrap support to Label widgets. Doesn't yet negotiate autosize or Center/Bottom TextAlignments perfectly. --- Userland/Libraries/LibGUI/Label.cpp | 95 ++++++++++++++++++++++++++--- Userland/Libraries/LibGUI/Label.h | 8 ++- 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/Userland/Libraries/LibGUI/Label.cpp b/Userland/Libraries/LibGUI/Label.cpp index de4508f421..266f77b9dc 100644 --- a/Userland/Libraries/LibGUI/Label.cpp +++ b/Userland/Libraries/LibGUI/Label.cpp @@ -47,6 +47,7 @@ Label::Label(String text) REGISTER_STRING_PROPERTY("text", text, set_text); REGISTER_BOOL_PROPERTY("autosize", is_autosize, set_autosize); + REGISTER_BOOL_PROPERTY("word_wrap", is_word_wrap, set_word_wrap); } Label::~Label() @@ -62,6 +63,15 @@ void Label::set_autosize(bool autosize) size_to_fit(); } +void Label::set_word_wrap(bool wrap) +{ + if (m_word_wrap == wrap) + return; + m_word_wrap = wrap; + if (is_word_wrap()) + wrap_text(); +} + void Label::set_icon(const Gfx::Bitmap* icon) { if (m_icon == icon) @@ -75,19 +85,21 @@ void Label::set_text(String text) if (text == m_text) return; m_text = move(text); + if (is_word_wrap()) + wrap_text(); if (m_autosize) size_to_fit(); update(); did_change_text(); } -Gfx::IntRect Label::text_rect() const +Gfx::IntRect Label::text_rect(size_t line) const { int indent = 0; if (frame_thickness() > 0) indent = font().glyph_width('x') / 2; auto rect = frame_inner_rect(); - rect.move_by(indent, 0); + rect.move_by(indent, line * (font().glyph_height() + 1)); rect.set_width(rect.width() - indent * 2); return rect; } @@ -107,15 +119,30 @@ void Label::paint_event(PaintEvent& event) painter.blit(icon_location, *m_icon, m_icon->rect()); } } + if (text().is_empty()) return; - auto text_rect = this->text_rect(); - if (is_enabled()) { - painter.draw_text(text_rect, text(), m_text_alignment, palette().color(foreground_role()), Gfx::TextElision::Right); + if (is_word_wrap()) { + wrap_text(); + for (size_t i = 0; i < m_lines.size(); i++) { + auto& line = m_lines[i]; + auto text_rect = this->text_rect(i); + if (is_enabled()) { + painter.draw_text(text_rect, line, m_text_alignment, palette().color(foreground_role()), Gfx::TextElision::None); + } else { + painter.draw_text(text_rect.translated(1, 1), line, font(), text_alignment(), Color::White, Gfx::TextElision::Right); + painter.draw_text(text_rect, line, font(), text_alignment(), Color::from_rgb(0x808080), Gfx::TextElision::Right); + } + } } else { - painter.draw_text(text_rect.translated(1, 1), text(), font(), text_alignment(), Color::White, Gfx::TextElision::Right); - painter.draw_text(text_rect, text(), font(), text_alignment(), Color::from_rgb(0x808080), Gfx::TextElision::Right); + auto text_rect = this->text_rect(); + if (is_enabled()) { + painter.draw_text(text_rect, text(), m_text_alignment, palette().color(foreground_role()), Gfx::TextElision::Right); + } else { + painter.draw_text(text_rect.translated(1, 1), text(), font(), text_alignment(), Color::White, Gfx::TextElision::Right); + painter.draw_text(text_rect, text(), font(), text_alignment(), Color::from_rgb(0x808080), Gfx::TextElision::Right); + } } } @@ -124,4 +151,58 @@ void Label::size_to_fit() set_fixed_width(font().width(m_text)); } +void Label::wrap_text() +{ + Vector words; + Optional start; + for (size_t i = 0; i < m_text.length(); i++) { + switch (m_text[i]) { + case '\n': + case '\r': + case '\t': + case ' ': { + if (start.has_value()) + words.append(m_text.substring(start.value(), i - start.value())); + start.clear(); + continue; + } + default: { + if (!start.has_value()) + start = i; + } + } + } + + if (start.has_value()) + words.append(m_text.substring(start.value(), m_text.length() - start.value())); + + auto rect = frame_inner_rect(); + if (frame_thickness() > 0) + rect.set_width(rect.width() - font().glyph_width('x')); + + Vector lines; + StringBuilder builder; + int line_width = 0; + for (auto& word : words) { + int word_width = font().width(word); + if (line_width > 0) + word_width += font().glyph_width('x'); + if (line_width + word_width > rect.width()) { + lines.append(builder.to_string()); + builder.clear(); + line_width = 0; + } + if (line_width > 0) + builder.append(' '); + builder.append(word); + line_width += word_width; + } + + auto last_line = builder.to_string(); + if (!last_line.is_empty()) + lines.append(last_line); + + m_lines = lines; +} + } diff --git a/Userland/Libraries/LibGUI/Label.h b/Userland/Libraries/LibGUI/Label.h index df17f28545..983bce37c1 100644 --- a/Userland/Libraries/LibGUI/Label.h +++ b/Userland/Libraries/LibGUI/Label.h @@ -53,7 +53,10 @@ public: bool is_autosize() const { return m_autosize; } void set_autosize(bool); - Gfx::IntRect text_rect() const; + bool is_word_wrap() const { return m_word_wrap; } + void set_word_wrap(bool); + + Gfx::IntRect text_rect(size_t line = 0) const; protected: explicit Label(String text = {}); @@ -63,12 +66,15 @@ protected: private: void size_to_fit(); + void wrap_text(); String m_text; RefPtr m_icon; Gfx::TextAlignment m_text_alignment { Gfx::TextAlignment::Center }; bool m_should_stretch_icon { false }; bool m_autosize { false }; + bool m_word_wrap { false }; + Vector m_lines; }; }