LibGUI: Implement very bad widget system :D
This commit is contained in:
parent
4695fa061d
commit
d73a667437
|
@ -1,5 +1,11 @@
|
||||||
set(LIBGUI_SOURCES
|
set(LIBGUI_SOURCES
|
||||||
Texture.cpp
|
Texture.cpp
|
||||||
|
Widget/Button.cpp
|
||||||
|
Widget/Grid.cpp
|
||||||
|
Widget/Label.cpp
|
||||||
|
Widget/RoundedWidget.cpp
|
||||||
|
Widget/TextArea.cpp
|
||||||
|
Widget/Widget.cpp
|
||||||
Window.cpp
|
Window.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
#include <LibGUI/Widget/Button.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<Button>> Button::create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry)
|
||||||
|
{
|
||||||
|
auto* button_ptr = new Button(parent, geometry);
|
||||||
|
if (button_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto button = BAN::RefPtr<Button>::adopt(button_ptr);
|
||||||
|
TRY(button->initialize(color_invisible));
|
||||||
|
TRY(button->m_text.append(text));
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Button::set_text(BAN::StringView text)
|
||||||
|
{
|
||||||
|
m_text.clear();
|
||||||
|
TRY(m_text.append(text));
|
||||||
|
if (is_shown())
|
||||||
|
show();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::update_impl()
|
||||||
|
{
|
||||||
|
const bool hover_color = is_hovered() && !is_child_hovered();
|
||||||
|
if (hover_color != m_hover_state)
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::show_impl()
|
||||||
|
{
|
||||||
|
m_hover_state = is_hovered() && !is_child_hovered();
|
||||||
|
|
||||||
|
const auto& font = default_font();
|
||||||
|
const int32_t text_h = font.height();
|
||||||
|
const int32_t text_w = font.width() * m_text.size();
|
||||||
|
|
||||||
|
const int32_t off_x = (static_cast<int32_t>(width()) - text_w) / 2;
|
||||||
|
const int32_t off_y = (static_cast<int32_t>(height()) - text_h) / 2;
|
||||||
|
|
||||||
|
m_texture.fill(m_hover_state ? m_style.color_hovered : m_style.color_normal);
|
||||||
|
m_texture.draw_text(m_text, font, off_x, off_y, m_style.color_text);
|
||||||
|
|
||||||
|
RoundedWidget::style() = m_style;
|
||||||
|
RoundedWidget::show_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Button::on_mouse_button_impl(LibGUI::EventPacket::MouseButtonEvent::event_t event)
|
||||||
|
{
|
||||||
|
if (event.pressed && event.button == LibInput::MouseButton::Left && m_click_callback)
|
||||||
|
m_click_callback();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <LibGUI/Widget/Grid.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<Grid>> Grid::create(BAN::RefPtr<Widget> parent, uint32_t cols, uint32_t rows, uint32_t color, Rectangle geometry)
|
||||||
|
{
|
||||||
|
auto* grid_ptr = new Grid(parent, geometry, cols, rows);
|
||||||
|
if (grid_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto grid = BAN::RefPtr<Grid>::adopt(grid_ptr);
|
||||||
|
TRY(grid->initialize(color));
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget::Rectangle Grid::grid_element_area(const GridElement& element) const
|
||||||
|
{
|
||||||
|
const uint32_t this_x = element.col * width() / m_cols;
|
||||||
|
const uint32_t this_y = element.row * height() / m_rows;
|
||||||
|
|
||||||
|
const uint32_t next_x = (element.col + element.col_span) * width() / m_cols;
|
||||||
|
const uint32_t next_y = (element.row + element.row_span) * height() / m_rows;
|
||||||
|
|
||||||
|
return Widget::Rectangle {
|
||||||
|
.x = static_cast<int32_t>(this_x),
|
||||||
|
.y = static_cast<int32_t>(this_y),
|
||||||
|
.w = next_x - this_x,
|
||||||
|
.h = next_y - this_y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Grid::update_geometry_impl()
|
||||||
|
{
|
||||||
|
for (auto& grid_element : m_grid_elements)
|
||||||
|
TRY(grid_element.widget->set_fixed_geometry(grid_element_area(grid_element)));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Grid::set_widget_position(BAN::RefPtr<Widget> widget, uint32_t col, uint32_t col_span, uint32_t row, uint32_t row_span)
|
||||||
|
{
|
||||||
|
if (col_span == 0 || row_span == 0)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
if (col + col_span > m_cols)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
if (row + row_span > m_rows)
|
||||||
|
return BAN::Error::from_errno(EINVAL);
|
||||||
|
ASSERT(widget->parent() == this);
|
||||||
|
TRY(m_grid_elements.emplace_back(widget, col, col_span, row, row_span));
|
||||||
|
TRY(widget->set_fixed_geometry(grid_element_area(m_grid_elements.back())));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
#include <LibGUI/Widget/Label.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<Label>> Label::create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry)
|
||||||
|
{
|
||||||
|
auto* label_ptr = new Label(parent, geometry);
|
||||||
|
if (label_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto label = BAN::RefPtr<Label>::adopt(label_ptr);
|
||||||
|
TRY(label->initialize(color_invisible));
|
||||||
|
TRY(label->m_text.append(text));
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Label::set_text(BAN::StringView text)
|
||||||
|
{
|
||||||
|
m_text.clear();
|
||||||
|
TRY(m_text.append(text));
|
||||||
|
if (is_shown())
|
||||||
|
show();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Label::show_impl()
|
||||||
|
{
|
||||||
|
const auto& font = default_font();
|
||||||
|
const int32_t text_h = font.height();
|
||||||
|
const int32_t text_w = font.width() * m_text.size();
|
||||||
|
|
||||||
|
const int32_t off_x = (static_cast<int32_t>(width()) - text_w) / 2;
|
||||||
|
const int32_t off_y = (static_cast<int32_t>(height()) - text_h) / 2;
|
||||||
|
|
||||||
|
m_texture.fill(m_style.color_normal);
|
||||||
|
m_texture.draw_text(m_text, font, off_x, off_y, m_style.color_text);
|
||||||
|
|
||||||
|
RoundedWidget::style() = m_style;
|
||||||
|
RoundedWidget::show_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
#include <LibGUI/Widget/RoundedWidget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
bool RoundedWidget::contains(Point point) const
|
||||||
|
{
|
||||||
|
if (!Widget::contains(point))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto is_outside_corner =
|
||||||
|
[this]<uint8_t quadrant>(Point point) -> bool
|
||||||
|
{
|
||||||
|
const auto radius = m_style.corner_radius;
|
||||||
|
const uint32_t base_x = (quadrant % 2) ? m_texture.width() - radius - 1 : 0;
|
||||||
|
const uint32_t base_y = (quadrant / 2) ? m_texture.height() - radius - 1 : 0;
|
||||||
|
|
||||||
|
if (point.x < static_cast<int32_t>(base_x) || point.x > static_cast<int32_t>(base_x + radius))
|
||||||
|
return false;
|
||||||
|
if (point.y < static_cast<int32_t>(base_y) || point.y > static_cast<int32_t>(base_y + radius))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const uint32_t x_off = point.x - base_x;
|
||||||
|
const uint32_t y_off = point.y - base_y;
|
||||||
|
|
||||||
|
const uint32_t dx = ((quadrant % 2) ? x_off : radius - x_off);
|
||||||
|
const uint32_t dy = ((quadrant / 2) ? y_off : radius - y_off);
|
||||||
|
const uint32_t distance = dx * dx + dy * dy;
|
||||||
|
|
||||||
|
return distance >= radius * radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (is_outside_corner.operator()<0>(point))
|
||||||
|
return false;
|
||||||
|
if (is_outside_corner.operator()<1>(point))
|
||||||
|
return false;
|
||||||
|
if (is_outside_corner.operator()<2>(point))
|
||||||
|
return false;
|
||||||
|
if (is_outside_corner.operator()<3>(point))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RoundedWidget::show_impl()
|
||||||
|
{
|
||||||
|
if (m_style.border_width)
|
||||||
|
{
|
||||||
|
m_texture.fill_rect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
m_texture.width(),
|
||||||
|
m_style.border_width,
|
||||||
|
m_style.color_border
|
||||||
|
);
|
||||||
|
m_texture.fill_rect(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
m_style.border_width,
|
||||||
|
m_texture.height(),
|
||||||
|
m_style.color_border
|
||||||
|
);
|
||||||
|
m_texture.fill_rect(
|
||||||
|
0,
|
||||||
|
m_texture.height() - m_style.border_width,
|
||||||
|
m_texture.width(),
|
||||||
|
m_style.border_width,
|
||||||
|
m_style.color_border
|
||||||
|
);
|
||||||
|
m_texture.fill_rect(
|
||||||
|
m_texture.width() - m_style.border_width,
|
||||||
|
0,
|
||||||
|
m_style.border_width,
|
||||||
|
m_texture.height(),
|
||||||
|
m_style.color_border
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_style.corner_radius)
|
||||||
|
{
|
||||||
|
const auto draw_corner =
|
||||||
|
[this]<uint8_t quadrant>()
|
||||||
|
{
|
||||||
|
const auto radius = m_style.corner_radius;
|
||||||
|
const uint32_t base_x = (quadrant % 2) ? m_texture.width() - radius - 1 : 0;
|
||||||
|
const uint32_t base_y = (quadrant / 2) ? m_texture.height() - radius - 1 : 0;
|
||||||
|
const uint32_t distance_max = radius * radius;
|
||||||
|
const uint32_t distance_min = (radius - m_style.border_width) * (radius - m_style.border_width);
|
||||||
|
|
||||||
|
for (uint32_t y_off = 0; y_off <= radius; y_off++)
|
||||||
|
{
|
||||||
|
for (uint32_t x_off = 0; x_off <= radius; x_off++)
|
||||||
|
{
|
||||||
|
const uint32_t dx = ((quadrant % 2) ? x_off : radius - x_off);
|
||||||
|
const uint32_t dy = ((quadrant / 2) ? y_off : radius - y_off);
|
||||||
|
const uint32_t distance = dx * dx + dy * dy;
|
||||||
|
|
||||||
|
if (base_x + x_off >= m_texture.width())
|
||||||
|
continue;
|
||||||
|
if (base_y + y_off >= m_texture.height())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (distance > distance_max)
|
||||||
|
m_texture.set_pixel(base_x + x_off, base_y + y_off, color_invisible);
|
||||||
|
else if (distance >= distance_min)
|
||||||
|
m_texture.set_pixel(base_x + x_off, base_y + y_off, m_style.color_border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
draw_corner.operator()<0>();
|
||||||
|
draw_corner.operator()<1>();
|
||||||
|
draw_corner.operator()<2>();
|
||||||
|
draw_corner.operator()<3>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
#include <LibFont/Font.h>
|
||||||
|
#include <LibGUI/Widget/TextArea.h>
|
||||||
|
#include <LibGUI/Window.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
BAN::ErrorOr<BAN::RefPtr<TextArea>> TextArea::create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry)
|
||||||
|
{
|
||||||
|
auto* text_area_ptr = new TextArea(parent, geometry);
|
||||||
|
if (text_area_ptr == nullptr)
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
auto text_area = BAN::RefPtr<TextArea>::adopt(text_area_ptr);
|
||||||
|
TRY(text_area->initialize(color_invisible));
|
||||||
|
TRY(text_area->set_text(text));
|
||||||
|
return text_area;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TextArea::set_text(BAN::StringView text)
|
||||||
|
{
|
||||||
|
m_text.clear();
|
||||||
|
TRY(m_text.append(text));
|
||||||
|
TRY(wrap_text());
|
||||||
|
if (is_shown())
|
||||||
|
show();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t TextArea::get_required_height() const
|
||||||
|
{
|
||||||
|
auto& font = default_font();
|
||||||
|
return m_wrapped_text.size() * font.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TextArea::wrap_text()
|
||||||
|
{
|
||||||
|
m_wrapped_text.clear();
|
||||||
|
|
||||||
|
if (width() == 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto& font = default_font();
|
||||||
|
|
||||||
|
const uint32_t total_columns = width() / font.width();
|
||||||
|
ASSERT(total_columns != 0);
|
||||||
|
|
||||||
|
TRY(m_wrapped_text.emplace_back());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_text.size(); i++)
|
||||||
|
{
|
||||||
|
if (m_text[i] == '\n')
|
||||||
|
TRY(m_wrapped_text.emplace_back());
|
||||||
|
else if (isspace(m_text[i]) && m_wrapped_text.back().size() == 0)
|
||||||
|
;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TRY(m_wrapped_text.back().push_back(m_text[i]));
|
||||||
|
|
||||||
|
if (i + 1 < m_text.size() && isspace(m_text[i]) && !isspace(m_text[i + 1]))
|
||||||
|
{
|
||||||
|
size_t word_len = 0;
|
||||||
|
for (size_t j = i + 1; j < m_text.size() && !isspace(m_text[j]); j++)
|
||||||
|
word_len++;
|
||||||
|
if (word_len <= total_columns && m_wrapped_text.back().size() + word_len > total_columns)
|
||||||
|
TRY(m_wrapped_text.emplace_back());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_wrapped_text.back().size() >= total_columns)
|
||||||
|
TRY(m_wrapped_text.emplace_back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> TextArea::update_geometry_impl()
|
||||||
|
{
|
||||||
|
TRY(wrap_text());
|
||||||
|
return Widget::update_geometry_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextArea::show_impl()
|
||||||
|
{
|
||||||
|
const auto& font = default_font();
|
||||||
|
|
||||||
|
m_texture.fill(m_style.color_normal);
|
||||||
|
for (int32_t row = 0; row < static_cast<int32_t>(m_wrapped_text.size()); row++)
|
||||||
|
m_texture.draw_text(m_wrapped_text[row].sv(), font, 0, row * font.height(), m_style.color_text);
|
||||||
|
|
||||||
|
RoundedWidget::style() = m_style;
|
||||||
|
RoundedWidget::show_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,241 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -113,6 +113,17 @@ namespace LibGUI
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> Window::set_root_widget(BAN::RefPtr<Widget::Widget> widget)
|
||||||
|
{
|
||||||
|
TRY(widget->set_fixed_geometry({ 0, 0, m_width, m_height }));
|
||||||
|
m_root_widget = widget;
|
||||||
|
m_root_widget->show();
|
||||||
|
const auto invalidated = m_root_widget->render(m_texture, { 0, 0 }, { 0, 0, m_width, m_height });
|
||||||
|
if (invalidated.w && invalidated.h)
|
||||||
|
invalidate(invalidated.x, invalidated.y, invalidated.w, invalidated.h);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void Window::invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height)
|
void Window::invalidate(int32_t x, int32_t y, uint32_t width, uint32_t height)
|
||||||
{
|
{
|
||||||
if (!m_texture.clamp_to_texture(x, y, width, height))
|
if (!m_texture.clamp_to_texture(x, y, width, height))
|
||||||
|
@ -168,6 +179,28 @@ namespace LibGUI
|
||||||
return on_socket_error(__FUNCTION__);
|
return on_socket_error(__FUNCTION__);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::set_cursor_visible(bool visible)
|
||||||
|
{
|
||||||
|
auto attributes = m_attributes;
|
||||||
|
if (attributes.cursor_visible == visible)
|
||||||
|
return;
|
||||||
|
attributes.cursor_visible = visible;
|
||||||
|
set_attributes(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::set_cursor(uint32_t width, uint32_t height, BAN::Span<uint32_t> pixels)
|
||||||
|
{
|
||||||
|
WindowPacket::WindowSetCursor packet;
|
||||||
|
packet.width = width;
|
||||||
|
packet.height = height;
|
||||||
|
MUST(packet.pixels.resize(pixels.size()));
|
||||||
|
for (size_t i = 0; i < packet.pixels.size(); i++)
|
||||||
|
packet.pixels[i] = pixels[i];
|
||||||
|
|
||||||
|
if (auto ret = packet.send_serialized(m_server_fd); ret.is_error())
|
||||||
|
return on_socket_error(__FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
void Window::set_min_size(uint32_t width, uint32_t height)
|
void Window::set_min_size(uint32_t width, uint32_t height)
|
||||||
{
|
{
|
||||||
WindowPacket::WindowSetMinSize packet;
|
WindowPacket::WindowSetMinSize packet;
|
||||||
|
@ -238,6 +271,9 @@ namespace LibGUI
|
||||||
|
|
||||||
TRY(m_texture.resize(event.width, event.height));
|
TRY(m_texture.resize(event.width, event.height));
|
||||||
|
|
||||||
|
if (m_root_widget)
|
||||||
|
TRY(m_root_widget->set_fixed_geometry({ 0, 0, event.width, event.height }));
|
||||||
|
|
||||||
void* framebuffer_addr = smo_map(event.smo_key);
|
void* framebuffer_addr = smo_map(event.smo_key);
|
||||||
if (framebuffer_addr == nullptr)
|
if (framebuffer_addr == nullptr)
|
||||||
return BAN::Error::from_errno(errno);
|
return BAN::Error::from_errno(errno);
|
||||||
|
@ -304,13 +340,27 @@ namespace LibGUI
|
||||||
m_key_event_callback(TRY_OR_BREAK(EventPacket::KeyEvent::deserialize(packet_data.span())).event);
|
m_key_event_callback(TRY_OR_BREAK(EventPacket::KeyEvent::deserialize(packet_data.span())).event);
|
||||||
break;
|
break;
|
||||||
case PacketType::MouseButtonEvent:
|
case PacketType::MouseButtonEvent:
|
||||||
|
{
|
||||||
|
auto event = TRY_OR_BREAK(EventPacket::MouseButtonEvent::deserialize(packet_data.span())).event;
|
||||||
if (m_mouse_button_event_callback)
|
if (m_mouse_button_event_callback)
|
||||||
m_mouse_button_event_callback(TRY_OR_BREAK(EventPacket::MouseButtonEvent::deserialize(packet_data.span())).event);
|
m_mouse_button_event_callback(event);
|
||||||
|
if (m_root_widget)
|
||||||
|
m_root_widget->on_mouse_button(event);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case PacketType::MouseMoveEvent:
|
case PacketType::MouseMoveEvent:
|
||||||
|
{
|
||||||
|
auto event = TRY_OR_BREAK(EventPacket::MouseMoveEvent::deserialize(packet_data.span())).event;
|
||||||
if (m_mouse_move_event_callback)
|
if (m_mouse_move_event_callback)
|
||||||
m_mouse_move_event_callback(TRY_OR_BREAK(EventPacket::MouseMoveEvent::deserialize(packet_data.span())).event);
|
m_mouse_move_event_callback(event);
|
||||||
|
if (m_root_widget)
|
||||||
|
{
|
||||||
|
m_root_widget->before_mouse_move();
|
||||||
|
m_root_widget->on_mouse_move(event);
|
||||||
|
m_root_widget->after_mouse_move();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case PacketType::MouseScrollEvent:
|
case PacketType::MouseScrollEvent:
|
||||||
if (m_mouse_scroll_event_callback)
|
if (m_mouse_scroll_event_callback)
|
||||||
m_mouse_scroll_event_callback(TRY_OR_BREAK(EventPacket::MouseScrollEvent::deserialize(packet_data.span())).event);
|
m_mouse_scroll_event_callback(TRY_OR_BREAK(EventPacket::MouseScrollEvent::deserialize(packet_data.span())).event);
|
||||||
|
@ -320,6 +370,13 @@ namespace LibGUI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#undef TRY_OR_BREAK
|
#undef TRY_OR_BREAK
|
||||||
|
|
||||||
|
if (m_root_widget)
|
||||||
|
{
|
||||||
|
const auto invalidated = m_root_widget->render(m_texture, { 0, 0 }, { 0, 0, m_width, m_height });
|
||||||
|
if (invalidated.w && invalidated.h)
|
||||||
|
invalidate(invalidated.x, invalidated.y, invalidated.w, invalidated.h);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Function.h>
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/RoundedWidget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class Button : public RoundedWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Style : RoundedWidget::Style
|
||||||
|
{
|
||||||
|
Style()
|
||||||
|
: RoundedWidget::Style()
|
||||||
|
, color_hovered(0x808080)
|
||||||
|
, color_text(0x000000)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint32_t color_hovered;
|
||||||
|
uint32_t color_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<Button>> create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry = {});
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> set_text(BAN::StringView);
|
||||||
|
|
||||||
|
Style& style() { return m_style; }
|
||||||
|
const Style& style() const { return m_style; }
|
||||||
|
|
||||||
|
void set_click_callback(BAN::Function<void()> callback) { m_click_callback = callback; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Button(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: RoundedWidget(parent, area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void update_impl() override;
|
||||||
|
void show_impl() override;
|
||||||
|
|
||||||
|
bool on_mouse_button_impl(LibGUI::EventPacket::MouseButtonEvent::event_t) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Style m_style;
|
||||||
|
bool m_hover_state { false };
|
||||||
|
BAN::String m_text;
|
||||||
|
|
||||||
|
BAN::Function<void()> m_click_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/Widget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class Grid : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<Grid>> create(BAN::RefPtr<Widget> parent, uint32_t cols, uint32_t rows, uint32_t color = color_invisible, Rectangle geometry = {});
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> set_widget_position(BAN::RefPtr<Widget> widget, uint32_t col, uint32_t col_span, uint32_t row, uint32_t row_span);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Grid(BAN::RefPtr<Widget> parent, Rectangle geometry, uint32_t cols, uint32_t rows)
|
||||||
|
: Widget(parent, geometry)
|
||||||
|
, m_cols(cols)
|
||||||
|
, m_rows(rows)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> update_geometry_impl() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct GridElement
|
||||||
|
{
|
||||||
|
BAN::RefPtr<Widget> widget;
|
||||||
|
uint32_t col;
|
||||||
|
uint32_t col_span;
|
||||||
|
uint32_t row;
|
||||||
|
uint32_t row_span;
|
||||||
|
};
|
||||||
|
|
||||||
|
Rectangle grid_element_area(const GridElement& element) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const uint32_t m_cols;
|
||||||
|
const uint32_t m_rows;
|
||||||
|
BAN::Vector<GridElement> m_grid_elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/RoundedWidget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class Label : public RoundedWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Style : RoundedWidget::Style
|
||||||
|
{
|
||||||
|
Style()
|
||||||
|
: RoundedWidget::Style()
|
||||||
|
, color_text(0x000000)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint32_t color_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<Label>> create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry = {});
|
||||||
|
|
||||||
|
BAN::StringView text() const { return m_text; }
|
||||||
|
BAN::ErrorOr<void> set_text(BAN::StringView);
|
||||||
|
|
||||||
|
Style& style() { return m_style; }
|
||||||
|
const Style& style() const { return m_style; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Label(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: RoundedWidget(parent, area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void show_impl() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Style m_style;
|
||||||
|
BAN::String m_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/Widget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class RoundedWidget : public Widget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Style
|
||||||
|
{
|
||||||
|
Style(uint32_t color_normal = 0xA0A0A0, uint32_t border_width = 1, uint32_t color_border = 0x000000, uint32_t corner_radius = 5)
|
||||||
|
: color_normal(color_normal)
|
||||||
|
, border_width(border_width)
|
||||||
|
, color_border(color_border)
|
||||||
|
, corner_radius(corner_radius)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint32_t color_normal;
|
||||||
|
uint32_t border_width;
|
||||||
|
uint32_t color_border;
|
||||||
|
uint32_t corner_radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
Style& style() { return m_style; }
|
||||||
|
const Style& style() const { return m_style; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
RoundedWidget(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: Widget(parent, area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool contains(Point point) const override;
|
||||||
|
|
||||||
|
void show_impl() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Style m_style;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/StringView.h>
|
||||||
|
|
||||||
|
#include <LibGUI/Widget/RoundedWidget.h>
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class TextArea : public RoundedWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Style : RoundedWidget::Style
|
||||||
|
{
|
||||||
|
Style()
|
||||||
|
: RoundedWidget::Style()
|
||||||
|
, color_text(0x000000)
|
||||||
|
{}
|
||||||
|
|
||||||
|
uint32_t color_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<TextArea>> create(BAN::RefPtr<Widget> parent, BAN::StringView text, Rectangle geometry = {});
|
||||||
|
|
||||||
|
BAN::StringView text() const { return m_text; }
|
||||||
|
BAN::ErrorOr<void> set_text(BAN::StringView);
|
||||||
|
|
||||||
|
uint32_t get_required_height() const;
|
||||||
|
|
||||||
|
Style& style() { return m_style; }
|
||||||
|
const Style& style() const { return m_style; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TextArea(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: RoundedWidget(parent, area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> wrap_text();
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> update_geometry_impl() override;
|
||||||
|
void show_impl() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Style m_style;
|
||||||
|
BAN::String m_text;
|
||||||
|
BAN::Vector<BAN::String> m_wrapped_text;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/RefPtr.h>
|
||||||
|
|
||||||
|
#include <LibGUI/Texture.h>
|
||||||
|
#include <LibGUI/Packet.h>
|
||||||
|
|
||||||
|
namespace LibGUI { class Window; }
|
||||||
|
|
||||||
|
namespace LibGUI::Widget
|
||||||
|
{
|
||||||
|
|
||||||
|
class Widget : public BAN::RefCounted<Widget>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr uint32_t color_invisible = Texture::color_invisible;
|
||||||
|
|
||||||
|
struct Point
|
||||||
|
{
|
||||||
|
int32_t x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FloatRectangle
|
||||||
|
{
|
||||||
|
float x, y;
|
||||||
|
float w, h;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rectangle
|
||||||
|
{
|
||||||
|
int32_t x, y;
|
||||||
|
uint32_t w, h;
|
||||||
|
|
||||||
|
struct Bounds
|
||||||
|
{
|
||||||
|
int32_t min_x, min_y;
|
||||||
|
int32_t max_x, max_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool contains(Point point) const
|
||||||
|
{
|
||||||
|
if (point.x < x || point.x >= x + static_cast<int32_t>(w))
|
||||||
|
return false;
|
||||||
|
if (point.y < y || point.y >= y + static_cast<int32_t>(h))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bounds bounds(Rectangle other) const
|
||||||
|
{
|
||||||
|
return Bounds {
|
||||||
|
.min_x = BAN::Math::max(x, other.x),
|
||||||
|
.min_y = BAN::Math::max(y, other.y),
|
||||||
|
.max_x = BAN::Math::min(x + static_cast<int32_t>(w), other.x + static_cast<int32_t>(other.w)),
|
||||||
|
.max_y = BAN::Math::min(y + static_cast<int32_t>(h), other.y + static_cast<int32_t>(other.h)),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
Rectangle overlap(Rectangle other) const
|
||||||
|
{
|
||||||
|
const auto min_x = BAN::Math::max(x, other.x);
|
||||||
|
const auto min_y = BAN::Math::max(y, other.y);
|
||||||
|
const auto max_x = BAN::Math::min(x + static_cast<int32_t>(w), other.x + static_cast<int32_t>(other.w));
|
||||||
|
const auto max_y = BAN::Math::min(y + static_cast<int32_t>(h), other.y + static_cast<int32_t>(other.h));
|
||||||
|
if (min_x >= max_x || min_y >= max_y)
|
||||||
|
return {};
|
||||||
|
return Rectangle {
|
||||||
|
.x = min_x,
|
||||||
|
.y = min_y,
|
||||||
|
.w = static_cast<uint32_t>(max_x - min_x),
|
||||||
|
.h = static_cast<uint32_t>(max_y - min_y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle bounding_box(Rectangle other) const
|
||||||
|
{
|
||||||
|
if (w == 0 || h == 0)
|
||||||
|
return other;
|
||||||
|
if (other.w == 0 || other.h == 0)
|
||||||
|
return *this;
|
||||||
|
const auto min_x = BAN::Math::min(x, other.x);
|
||||||
|
const auto min_y = BAN::Math::min(y, other.y);
|
||||||
|
const auto max_x = BAN::Math::max(x + static_cast<int32_t>(w), other.x + static_cast<int32_t>(other.w));
|
||||||
|
const auto max_y = BAN::Math::max(y + static_cast<int32_t>(h), other.y + static_cast<int32_t>(other.h));
|
||||||
|
return Rectangle {
|
||||||
|
.x = min_x,
|
||||||
|
.y = min_y,
|
||||||
|
.w = static_cast<uint32_t>(max_x - min_x),
|
||||||
|
.h = static_cast<uint32_t>(max_y - min_y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static BAN::ErrorOr<BAN::RefPtr<Widget>> create(BAN::RefPtr<Widget> parent, uint32_t color = color_invisible, Rectangle geometry = {});
|
||||||
|
|
||||||
|
static BAN::ErrorOr<void> set_default_font(BAN::StringView path);
|
||||||
|
static const LibFont::Font& default_font();
|
||||||
|
|
||||||
|
void show();
|
||||||
|
void hide();
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> set_fixed_geometry(Rectangle);
|
||||||
|
BAN::ErrorOr<void> set_relative_geometry(FloatRectangle);
|
||||||
|
|
||||||
|
BAN::RefPtr<Widget> parent() { return m_parent; }
|
||||||
|
|
||||||
|
uint32_t width() const { return m_fixed_area.w; }
|
||||||
|
uint32_t height() const { return m_fixed_area.h; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void before_mouse_move();
|
||||||
|
void after_mouse_move();
|
||||||
|
bool on_mouse_move(LibGUI::EventPacket::MouseMoveEvent::event_t);
|
||||||
|
|
||||||
|
bool on_mouse_button(LibGUI::EventPacket::MouseButtonEvent::event_t);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Widget(BAN::RefPtr<Widget> parent, Rectangle area)
|
||||||
|
: m_parent(parent)
|
||||||
|
, m_fixed_area(area)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> initialize(uint32_t color);
|
||||||
|
|
||||||
|
virtual bool contains(Point point) const { return Rectangle { 0, 0, width(), height() }.contains(point); }
|
||||||
|
|
||||||
|
bool is_hovered() const { return m_hovered; }
|
||||||
|
bool is_child_hovered() const;
|
||||||
|
|
||||||
|
bool is_shown() const { return m_shown; }
|
||||||
|
|
||||||
|
Rectangle render(Texture& output, Point parent_position, Rectangle out_area);
|
||||||
|
|
||||||
|
virtual void update_impl() {}
|
||||||
|
virtual void show_impl() {}
|
||||||
|
|
||||||
|
virtual BAN::ErrorOr<void> update_geometry_impl();
|
||||||
|
|
||||||
|
virtual void on_hover_change_impl(bool hovered) { (void)hovered; }
|
||||||
|
virtual bool on_mouse_move_impl(LibGUI::EventPacket::MouseMoveEvent::event_t) { return true; }
|
||||||
|
virtual bool on_mouse_button_impl(LibGUI::EventPacket::MouseButtonEvent::event_t) { return true; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Texture m_texture;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BAN::RefPtr<Widget> m_parent;
|
||||||
|
BAN::Vector<BAN::RefPtr<Widget>> m_children;
|
||||||
|
bool m_shown { false };
|
||||||
|
|
||||||
|
Rectangle m_fixed_area;
|
||||||
|
BAN::Optional<FloatRectangle> m_relative_area;
|
||||||
|
|
||||||
|
bool m_changed { false };
|
||||||
|
|
||||||
|
bool m_hovered { false };
|
||||||
|
bool m_old_hovered { false };
|
||||||
|
|
||||||
|
friend class LibGUI::Window;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <LibGUI/Packet.h>
|
#include <LibGUI/Packet.h>
|
||||||
#include <LibGUI/Texture.h>
|
#include <LibGUI/Texture.h>
|
||||||
|
#include <LibGUI/Widget/Widget.h>
|
||||||
|
|
||||||
namespace LibFont { class Font; }
|
namespace LibFont { class Font; }
|
||||||
|
|
||||||
|
@ -32,6 +33,9 @@ namespace LibGUI
|
||||||
|
|
||||||
static BAN::ErrorOr<BAN::UniqPtr<Window>> create(uint32_t width, uint32_t height, BAN::StringView title, Attributes attributes = default_attributes);
|
static BAN::ErrorOr<BAN::UniqPtr<Window>> create(uint32_t width, uint32_t height, BAN::StringView title, Attributes attributes = default_attributes);
|
||||||
|
|
||||||
|
BAN::ErrorOr<void> set_root_widget(BAN::RefPtr<Widget::Widget> widget);
|
||||||
|
BAN::RefPtr<Widget::Widget> root_widget() { return m_root_widget; }
|
||||||
|
|
||||||
Texture& texture() { return m_texture; }
|
Texture& texture() { return m_texture; }
|
||||||
const Texture& texture() const { return m_texture; }
|
const Texture& texture() const { return m_texture; }
|
||||||
|
|
||||||
|
@ -94,6 +98,7 @@ namespace LibGUI
|
||||||
uint32_t m_height { 0 };
|
uint32_t m_height { 0 };
|
||||||
|
|
||||||
Texture m_texture;
|
Texture m_texture;
|
||||||
|
BAN::RefPtr<Widget::Widget> m_root_widget;
|
||||||
|
|
||||||
BAN::Function<void()> m_socket_error_callback;
|
BAN::Function<void()> m_socket_error_callback;
|
||||||
BAN::Function<void()> m_close_window_event_callback;
|
BAN::Function<void()> m_close_window_event_callback;
|
||||||
|
|
Loading…
Reference in New Issue