Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "TextWidget.h"
28#include <AK/Optional.h>
29#include <AK/String.h>
30#include <AK/StringBuilder.h>
31#include <AK/Vector.h>
32#include <LibGUI/Painter.h>
33#include <LibGfx/Font.h>
34#include <LibGfx/Palette.h>
35
36TextWidget::TextWidget(const StringView& text)
37 : m_text(text)
38{
39 set_frame_thickness(0);
40 set_frame_shadow(Gfx::FrameShadow::Plain);
41 set_frame_shape(Gfx::FrameShape::NoFrame);
42}
43
44TextWidget::~TextWidget()
45{
46}
47
48void TextWidget::set_text(const StringView& text)
49{
50 if (text == m_text)
51 return;
52 m_text = text;
53 wrap_and_set_height();
54 update();
55}
56
57void TextWidget::paint_event(GUI::PaintEvent& event)
58{
59 GUI::Frame::paint_event(event);
60
61 GUI::Painter painter(*this);
62 painter.add_clip_rect(event.rect());
63
64 int indent = 0;
65 if (frame_thickness() > 0)
66 indent = font().glyph_width('x') / 2;
67
68 for (size_t i = 0; i < m_lines.size(); i++) {
69 auto& line = m_lines[i];
70
71 auto text_rect = frame_inner_rect();
72 text_rect.move_by(indent, i * m_line_height);
73 if (!line.is_empty())
74 text_rect.set_width(text_rect.width() - indent * 2);
75
76 if (is_enabled()) {
77 painter.draw_text(text_rect, line, m_text_alignment, palette().color(foreground_role()), Gfx::TextElision::None);
78 } else {
79 painter.draw_text(text_rect.translated(1, 1), line, font(), text_alignment(), Color::White, Gfx::TextElision::Right);
80 painter.draw_text(text_rect, line, font(), text_alignment(), Color::from_rgb(0x808080), Gfx::TextElision::Right);
81 }
82 }
83}
84
85void TextWidget::resize_event(GUI::ResizeEvent& event)
86{
87 wrap_and_set_height();
88 GUI::Widget::resize_event(event);
89}
90
91void TextWidget::wrap_and_set_height()
92{
93 Vector<String> words;
94 Optional<size_t> start;
95 for (size_t i = 0; i < m_text.length(); i++) {
96 auto ch = m_text[i];
97
98 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
99 if (start.has_value())
100 words.append(m_text.substring(start.value(), i - start.value()));
101 start.clear();
102 } else if (!start.has_value()) {
103 start = i;
104 }
105 }
106 if (start.has_value())
107 words.append(m_text.substring(start.value(), m_text.length() - start.value()));
108
109 auto rect = frame_inner_rect();
110 if (frame_thickness() > 0)
111 rect.set_width(rect.width() - font().glyph_width('x'));
112
113 StringBuilder builder;
114 Vector<String> lines;
115 int line_width = 0;
116 for (auto& word : words) {
117 int word_width = font().width(word);
118 if (line_width != 0)
119 word_width += font().glyph_width('x');
120
121 if (line_width + word_width > rect.width()) {
122 lines.append(builder.to_string());
123 builder.clear();
124 line_width = 0;
125 }
126
127 if (line_width != 0)
128 builder.append(' ');
129 builder.append(word);
130 line_width += word_width;
131 }
132 auto last_line = builder.to_string();
133 if (!last_line.is_empty()) {
134 lines.append(last_line);
135 }
136
137 m_lines = lines;
138
139 set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed);
140 set_preferred_size(0, m_lines.size() * m_line_height + frame_thickness() * 2);
141}