Serenity Operating System
1/*
2 * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/Utf8View.h>
9#include <LibGUI/Label.h>
10#include <LibGUI/Painter.h>
11#include <LibGfx/Bitmap.h>
12#include <LibGfx/Palette.h>
13#include <LibGfx/TextWrapping.h>
14
15REGISTER_WIDGET(GUI, Label)
16
17namespace GUI {
18
19Label::Label(DeprecatedString text)
20 : m_text(move(text))
21{
22 REGISTER_TEXT_ALIGNMENT_PROPERTY("text_alignment", text_alignment, set_text_alignment);
23 REGISTER_TEXT_WRAPPING_PROPERTY("text_wrapping", text_wrapping, set_text_wrapping);
24
25 set_preferred_size({ SpecialDimension::OpportunisticGrow });
26 set_min_height(22);
27
28 set_frame_thickness(0);
29 set_frame_shadow(Gfx::FrameShadow::Plain);
30 set_frame_shape(Gfx::FrameShape::NoFrame);
31
32 set_foreground_role(Gfx::ColorRole::WindowText);
33
34 REGISTER_STRING_PROPERTY("text", text, set_text);
35 REGISTER_BOOL_PROPERTY("autosize", is_autosize, set_autosize);
36 REGISTER_WRITE_ONLY_STRING_PROPERTY("icon", set_icon_from_path);
37}
38
39void Label::set_autosize(bool autosize, size_t padding)
40{
41 if (m_autosize == autosize && m_autosize_padding == padding)
42 return;
43 m_autosize = autosize;
44 m_autosize_padding = padding;
45 if (m_autosize)
46 size_to_fit();
47}
48
49void Label::set_icon(Gfx::Bitmap const* icon)
50{
51 if (m_icon == icon)
52 return;
53 m_icon = icon;
54 update();
55}
56
57void Label::set_icon_from_path(DeprecatedString const& path)
58{
59 auto maybe_bitmap = Gfx::Bitmap::load_from_file(path);
60 if (maybe_bitmap.is_error()) {
61 dbgln("Unable to load bitmap `{}` for label icon", path);
62 return;
63 }
64 set_icon(maybe_bitmap.release_value());
65}
66
67void Label::set_text(DeprecatedString text)
68{
69 if (text == m_text)
70 return;
71 m_text = move(text);
72
73 if (m_autosize)
74 size_to_fit();
75 update();
76 did_change_text();
77}
78
79Gfx::IntRect Label::text_rect() const
80{
81 return frame_inner_rect().shrunken(frame_thickness() > 0 ? font().glyph_width('x') : 0, 0);
82}
83
84void Label::paint_event(PaintEvent& event)
85{
86 Frame::paint_event(event);
87
88 Painter painter(*this);
89 painter.add_clip_rect(event.rect());
90
91 if (m_icon) {
92 if (m_should_stretch_icon) {
93 painter.draw_scaled_bitmap(frame_inner_rect(), *m_icon, m_icon->rect());
94 } else {
95 auto icon_location = frame_inner_rect().center().translated(-(m_icon->width() / 2), -(m_icon->height() / 2));
96 painter.blit(icon_location, *m_icon, m_icon->rect());
97 }
98 }
99
100 if (text().is_empty())
101 return;
102
103 auto text_rect = this->text_rect();
104 if (is_enabled()) {
105 painter.draw_text(text_rect, text(), text_alignment(), palette().color(foreground_role()), Gfx::TextElision::Right, text_wrapping());
106 } else {
107 painter.draw_text(text_rect.translated(1, 1), text(), font(), text_alignment(), palette().disabled_text_back(), Gfx::TextElision::Right, text_wrapping());
108 painter.draw_text(text_rect, text(), font(), text_alignment(), palette().disabled_text_front(), Gfx::TextElision::Right, text_wrapping());
109 }
110}
111
112void Label::size_to_fit()
113{
114 set_fixed_width(text_calculated_preferred_width());
115}
116
117int Label::text_calculated_preferred_width() const
118{
119 return static_cast<int>(ceilf(font().width(m_text))) + m_autosize_padding * 2;
120}
121
122int Label::text_calculated_preferred_height() const
123{
124 return static_cast<int>(ceilf(font().preferred_line_height()) * (m_text.count("\n"sv) + 1));
125}
126
127Optional<UISize> Label::calculated_preferred_size() const
128{
129 return GUI::UISize(text_calculated_preferred_width(), text_calculated_preferred_height());
130}
131
132}