瀏覽代碼

LibGUI: Add word wrapping to Labels

Adds basic word wrap support to Label widgets. Doesn't yet
negotiate autosize or Center/Bottom TextAlignments perfectly.
thankyouverycool 4 年之前
父節點
當前提交
0443cd4eed
共有 2 個文件被更改,包括 95 次插入8 次删除
  1. 88 7
      Userland/Libraries/LibGUI/Label.cpp
  2. 7 1
      Userland/Libraries/LibGUI/Label.h

+ 88 - 7
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<String> words;
+    Optional<size_t> 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<String> 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;
+}
+
 }

+ 7 - 1
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<Gfx::Bitmap> 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<String> m_lines;
 };
 
 }