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 <LibGUI/CheckBox.h>
9#include <LibGUI/Painter.h>
10#include <LibGfx/CharacterBitmap.h>
11#include <LibGfx/Font/Font.h>
12#include <LibGfx/Palette.h>
13#include <LibGfx/StylePainter.h>
14
15REGISTER_WIDGET(GUI, CheckBox)
16
17namespace GUI {
18
19CheckBox::CheckBox(String text)
20 : AbstractButton(move(text))
21{
22 REGISTER_BOOL_PROPERTY("autosize", is_autosize, set_autosize);
23
24 REGISTER_ENUM_PROPERTY(
25 "checkbox_position", checkbox_position, set_checkbox_position, CheckBoxPosition,
26 { CheckBoxPosition::Left, "Left" },
27 { CheckBoxPosition::Right, "Right" });
28
29 set_min_size(SpecialDimension::Shrink, SpecialDimension::Shrink);
30 set_preferred_size(SpecialDimension::OpportunisticGrow, SpecialDimension::Shrink);
31}
32
33int CheckBox::gap_between_box_and_rect() const
34{
35 return 6;
36}
37
38int CheckBox::horizontal_padding() const
39{
40 return 2;
41}
42
43Gfx::IntRect CheckBox::box_rect() const
44{
45 int box_size = max(13, height() - 10);
46
47 Gfx::IntRect box_rect {
48 0,
49 height() / 2 - box_size / 2 - 1,
50 box_size,
51 box_size,
52 };
53 if (m_checkbox_position == CheckBoxPosition::Right)
54 box_rect.set_right_without_resize(rect().right());
55
56 return box_rect;
57}
58
59void CheckBox::paint_event(PaintEvent& event)
60{
61 Painter painter(*this);
62 painter.add_clip_rect(event.rect());
63
64 auto box_rect = this->box_rect();
65 auto text_rect = rect();
66 if (m_checkbox_position == CheckBoxPosition::Left)
67 text_rect.set_left(box_rect.right() + 1 + gap_between_box_and_rect());
68 text_rect.set_width(static_cast<int>(ceilf(font().width(text()))));
69 text_rect.set_top(height() / 2 - font().pixel_size_rounded_up() / 2);
70 text_rect.set_height(font().pixel_size_rounded_up());
71
72 if (fill_with_background_color())
73 painter.fill_rect(rect(), palette().window());
74
75 if (is_enabled() && is_hovered())
76 painter.fill_rect(rect(), palette().hover_highlight());
77
78 Gfx::StylePainter::paint_check_box(painter, box_rect, palette(), is_enabled(), is_checked(), is_being_pressed());
79
80 paint_text(painter, text_rect, font(), Gfx::TextAlignment::TopLeft);
81
82 if (is_focused())
83 painter.draw_focus_rect(text_rect.inflated(6, 6), palette().focus_outline());
84}
85
86void CheckBox::click(unsigned)
87{
88 if (!is_enabled())
89 return;
90 set_checked(!is_checked());
91}
92
93void CheckBox::set_autosize(bool autosize)
94{
95 if (m_autosize == autosize)
96 return;
97 m_autosize = autosize;
98 if (m_autosize)
99 size_to_fit();
100}
101
102void CheckBox::size_to_fit()
103{
104 set_fixed_width(box_rect().width() + gap_between_box_and_rect() + static_cast<int>(ceilf(font().width(text()))) + horizontal_padding() * 2);
105}
106
107Optional<UISize> CheckBox::calculated_min_size() const
108{
109 auto const& font = this->font();
110 int width = box_rect().width();
111 int height = max(22, max(static_cast<int>(ceilf(font.pixel_size())) + 8, box_rect().height()));
112 return UISize(width, height);
113}
114
115}