Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, 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/BoxLayout.h>
9#include <LibGUI/Painter.h>
10#include <LibGUI/ResizeCorner.h>
11#include <LibGUI/Statusbar.h>
12#include <LibGUI/Window.h>
13#include <LibGfx/Palette.h>
14#include <LibGfx/StylePainter.h>
15
16REGISTER_WIDGET(GUI, Statusbar)
17
18namespace GUI {
19
20Statusbar::Statusbar(int count)
21{
22 set_fixed_height(18);
23 set_layout<HorizontalBoxLayout>(0, 2);
24
25 m_corner = add<ResizeCorner>();
26 set_segment_count(count);
27
28 REGISTER_STRING_PROPERTY("text", text, set_text);
29 REGISTER_INT_PROPERTY("segment_count", segment_count, set_segment_count);
30}
31
32NonnullRefPtr<Statusbar::Segment> Statusbar::create_segment()
33{
34 auto widget = Segment::construct();
35 insert_child_before(*widget, *m_corner);
36 return widget;
37}
38
39void Statusbar::child_event(Core::ChildEvent& event)
40{
41 auto& event_to_forward = event;
42 // To ensure that the ResizeCorner is always the last widget, and thus stays in the corner,
43 // we replace ChildAdded events that do not request specific placement with events that request placement before the corner
44 if (event.type() == Event::ChildAdded && is<Widget>(*event.child()) && !event.insertion_before_child()) {
45 Core::ChildEvent new_event(Event::ChildAdded, *event.child(), m_corner.ptr());
46 event_to_forward = new_event;
47 }
48
49 return Widget::child_event(event_to_forward);
50}
51
52void Statusbar::set_segment_count(size_t count)
53{
54 if (count <= 1)
55 count = 1;
56
57 for (auto i = m_segments.size(); i < count; i++) {
58 auto segment = create_segment();
59 m_segments.append(move(segment));
60 }
61}
62
63void Statusbar::update_segment(size_t index)
64{
65 auto& segment = m_segments.at(index);
66 if (segment->mode() == Segment::Mode::Auto) {
67 if (segment->restored_text().is_empty())
68 segment->set_visible(false);
69 else {
70 constexpr auto horizontal_padding { 10 };
71 auto width = font().width(segment->restored_text()) + horizontal_padding;
72 segment->set_restored_width(width);
73 segment->set_fixed_width(width);
74 }
75 } else if (segment->mode() == Segment::Mode::Fixed) {
76 if (segment->max_width().is_int()) {
77 segment->set_restored_width(segment->max_width().as_int());
78 segment->set_fixed_width(segment->max_width());
79 }
80 }
81
82 if (segment->override_text().is_null()) {
83 for (size_t i = 1; i < m_segments.size(); i++) {
84 if (!text(i).is_empty())
85 m_segments[i]->set_visible(true);
86 }
87 segment->set_text(String::from_utf8(segment->restored_text()).release_value_but_fixme_should_propagate_errors());
88 segment->set_frame_shape(Gfx::FrameShape::Panel);
89 if (segment->mode() != Segment::Mode::Proportional)
90 segment->set_fixed_width(segment->restored_width());
91 } else {
92 for (size_t i = 1; i < m_segments.size(); i++) {
93 if (!m_segments[i]->is_clickable())
94 m_segments[i]->set_visible(false);
95 }
96 segment->set_text(String::from_utf8(segment->override_text()).release_value_but_fixme_should_propagate_errors());
97 segment->set_frame_shape(Gfx::FrameShape::NoFrame);
98 if (segment->mode() != Segment::Mode::Proportional)
99 segment->set_fixed_width(SpecialDimension::Grow);
100 }
101}
102
103DeprecatedString Statusbar::text(size_t index) const
104{
105 return m_segments[index]->text().to_deprecated_string();
106}
107
108void Statusbar::set_text(DeprecatedString text)
109{
110 set_text(0, move(text));
111}
112
113void Statusbar::set_text(size_t index, DeprecatedString text)
114{
115 m_segments[index]->m_restored_text = move(text);
116 update_segment(index);
117}
118
119void Statusbar::set_override_text(DeprecatedString override_text)
120{
121 m_segments[0]->m_override_text = move(override_text);
122 update_segment(0);
123}
124
125void Statusbar::paint_event(PaintEvent& event)
126{
127 Painter painter(*this);
128 painter.add_clip_rect(event.rect());
129 painter.fill_rect(rect(), palette().button());
130}
131
132void Statusbar::resize_event(ResizeEvent& event)
133{
134 if (auto* window = this->window()) {
135 m_corner->set_visible(window->is_resizable() && !window->is_maximized());
136 }
137
138 Widget::resize_event(event);
139}
140
141Statusbar::Segment::Segment()
142{
143 set_fixed_height(18);
144 set_focus_policy(GUI::FocusPolicy::NoFocus);
145 set_button_style(Gfx::ButtonStyle::Tray);
146 set_text_alignment(Gfx::TextAlignment::CenterLeft);
147}
148
149void Statusbar::Segment::paint_event(PaintEvent& event)
150{
151 Painter painter(*this);
152 painter.add_clip_rect(event.rect());
153
154 Gfx::StylePainter::current().paint_frame(painter, rect(), palette(), m_shape, Gfx::FrameShadow::Sunken, m_thickness, spans_entire_window_horizontally());
155
156 if (is_clickable())
157 Button::paint_event(event);
158 else if (!text().is_empty())
159 painter.draw_text(rect().shrunken(font().max_glyph_width(), 0), text(), text_alignment(), palette().color(foreground_role()), Gfx::TextElision::Right, Gfx::TextWrapping::DontWrap);
160}
161
162void Statusbar::Segment::mousedown_event(MouseEvent& event)
163{
164 if (!is_clickable())
165 return;
166 Button::mousedown_event(event);
167}
168
169void Statusbar::Segment::mouseup_event(MouseEvent& event)
170{
171 if (!is_clickable())
172 return;
173 Button::mouseup_event(event);
174}
175
176}