242 lines
5.1 KiB
C++
242 lines
5.1 KiB
C++
#include <LibGUI/Widget/Widget.h>
|
|
#include <LibFont/Font.h>
|
|
|
|
namespace LibGUI::Widget
|
|
{
|
|
|
|
static BAN::Optional<LibFont::Font> s_default_font;
|
|
|
|
BAN::ErrorOr<void> Widget::set_default_font(BAN::StringView path)
|
|
{
|
|
s_default_font = TRY(LibFont::Font::load(path));
|
|
return {};
|
|
}
|
|
|
|
const LibFont::Font& Widget::default_font()
|
|
{
|
|
if (!s_default_font.has_value())
|
|
MUST(set_default_font("/usr/share/fonts/lat0-16.psfu"_sv));
|
|
return s_default_font.value();
|
|
}
|
|
|
|
BAN::ErrorOr<BAN::RefPtr<Widget>> Widget::create(BAN::RefPtr<Widget> parent, uint32_t color, Rectangle area)
|
|
{
|
|
auto* widget_ptr = new Widget(parent, area);
|
|
if (widget_ptr == nullptr)
|
|
return BAN::Error::from_errno(ENOMEM);
|
|
auto widget = BAN::RefPtr<Widget>::adopt(widget_ptr);
|
|
TRY(widget->initialize(color));
|
|
return widget;
|
|
}
|
|
|
|
BAN::ErrorOr<void> Widget::initialize(uint32_t color)
|
|
{
|
|
m_texture = TRY(Texture::create(width(), height(), color));
|
|
if (m_parent)
|
|
TRY(m_parent->m_children.push_back(this));
|
|
return {};
|
|
}
|
|
|
|
bool Widget::is_child_hovered() const
|
|
{
|
|
for (auto child : m_children)
|
|
if (child->m_hovered || child->is_child_hovered())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
BAN::ErrorOr<void> Widget::set_fixed_geometry(Rectangle area)
|
|
{
|
|
TRY(m_texture.resize(area.w, area.h));
|
|
m_fixed_area = area;
|
|
m_relative_area.clear();
|
|
|
|
TRY(update_geometry_impl());
|
|
|
|
if (is_shown())
|
|
show();
|
|
|
|
return {};
|
|
}
|
|
|
|
BAN::ErrorOr<void> Widget::set_relative_geometry(FloatRectangle area)
|
|
{
|
|
if (area.w < 0.0f || area.h < 0.0f)
|
|
return BAN::Error::from_errno(EINVAL);
|
|
ASSERT(m_parent);
|
|
|
|
TRY(set_fixed_geometry({
|
|
.x = static_cast<int32_t>(area.x * m_parent->width()),
|
|
.y = static_cast<int32_t>(area.y * m_parent->height()),
|
|
.w = static_cast<uint32_t>(area.w * m_parent->width()),
|
|
.h = static_cast<uint32_t>(area.h * m_parent->height()),
|
|
}));
|
|
|
|
m_relative_area = area;
|
|
return {};
|
|
}
|
|
|
|
BAN::ErrorOr<void> Widget::update_geometry_impl()
|
|
{
|
|
for (auto child : m_children)
|
|
{
|
|
if (!child->m_relative_area.has_value())
|
|
continue;
|
|
TRY(child->set_relative_geometry(child->m_relative_area.value()));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void Widget::show()
|
|
{
|
|
m_shown = true;
|
|
|
|
show_impl();
|
|
m_changed = true;
|
|
|
|
for (auto child : m_children)
|
|
if (child->is_shown())
|
|
child->show();
|
|
}
|
|
|
|
void Widget::hide()
|
|
{
|
|
if (!is_shown())
|
|
return;
|
|
m_shown = false;
|
|
|
|
auto root = m_parent;
|
|
while (root && root->m_parent)
|
|
root = root->m_parent;
|
|
if (root)
|
|
root->show();
|
|
}
|
|
|
|
void Widget::before_mouse_move()
|
|
{
|
|
if (!is_shown())
|
|
return;
|
|
|
|
m_old_hovered = m_hovered;
|
|
m_hovered = false;
|
|
|
|
for (auto child : m_children)
|
|
child->before_mouse_move();
|
|
}
|
|
|
|
void Widget::after_mouse_move()
|
|
{
|
|
if (!is_shown())
|
|
return;
|
|
|
|
if (m_old_hovered != m_hovered)
|
|
on_hover_change_impl(m_hovered);
|
|
|
|
for (auto child : m_children)
|
|
child->after_mouse_move();
|
|
}
|
|
|
|
bool Widget::on_mouse_move(LibGUI::EventPacket::MouseMoveEvent::event_t event)
|
|
{
|
|
if (!Rectangle { 0, 0, width(), height() }.contains({ event.x, event.y }))
|
|
return false;
|
|
if (!is_shown())
|
|
return false;
|
|
|
|
m_hovered = contains({ event.x, event.y });
|
|
|
|
for (auto child : m_children)
|
|
{
|
|
auto rel_event = event;
|
|
rel_event.x -= child->m_fixed_area.x;
|
|
rel_event.y -= child->m_fixed_area.y;
|
|
if (child->on_mouse_move(rel_event))
|
|
return true;
|
|
}
|
|
|
|
if (!m_hovered)
|
|
return false;
|
|
return on_mouse_move_impl(event);
|
|
}
|
|
|
|
bool Widget::on_mouse_button(LibGUI::EventPacket::MouseButtonEvent::event_t event)
|
|
{
|
|
if (!Rectangle { 0, 0, width(), height() }.contains({ event.x, event.y }))
|
|
return false;
|
|
if (!is_shown())
|
|
return false;
|
|
|
|
for (auto child : m_children)
|
|
{
|
|
auto rel_event = event;
|
|
rel_event.x -= child->m_fixed_area.x;
|
|
rel_event.y -= child->m_fixed_area.y;
|
|
if (child->on_mouse_button(rel_event))
|
|
return true;
|
|
}
|
|
|
|
if (!contains({ event.x, event.y }))
|
|
return false;
|
|
return on_mouse_button_impl(event);
|
|
}
|
|
|
|
Widget::Rectangle Widget::render(Texture& output, Point parent_position, Rectangle out_area)
|
|
{
|
|
if (!is_shown())
|
|
return {};
|
|
|
|
update_impl();
|
|
|
|
Rectangle invalidated {};
|
|
if (m_changed)
|
|
{
|
|
auto where_i_would_be = Rectangle {
|
|
.x = parent_position.x + m_fixed_area.x,
|
|
.y = parent_position.y + m_fixed_area.y,
|
|
.w = m_fixed_area.w,
|
|
.h = m_fixed_area.h,
|
|
};
|
|
|
|
auto where_to_draw = out_area.overlap(where_i_would_be);
|
|
|
|
if (where_to_draw.w && where_to_draw.h)
|
|
{
|
|
output.copy_texture(
|
|
m_texture,
|
|
where_to_draw.x,
|
|
where_to_draw.y,
|
|
where_to_draw.x - where_i_would_be.x,
|
|
where_to_draw.y - where_i_would_be.y,
|
|
where_to_draw.w,
|
|
where_to_draw.h
|
|
);
|
|
}
|
|
|
|
invalidated = where_to_draw;
|
|
m_changed = false;
|
|
}
|
|
|
|
out_area = out_area.overlap({
|
|
.x = parent_position.x + m_fixed_area.x,
|
|
.y = parent_position.y + m_fixed_area.y,
|
|
.w = m_fixed_area.w,
|
|
.h = m_fixed_area.h,
|
|
});
|
|
|
|
parent_position = {
|
|
.x = parent_position.x + m_fixed_area.x,
|
|
.y = parent_position.y + m_fixed_area.y,
|
|
};
|
|
|
|
for (auto child : m_children)
|
|
{
|
|
invalidated = invalidated.bounding_box(
|
|
child->render(output, parent_position, out_area)
|
|
);
|
|
}
|
|
|
|
return invalidated;
|
|
}
|
|
|
|
}
|