libui: Add basic widget and layout system =D

This commit is contained in:
apio 2023-08-15 12:28:47 +02:00
parent 0a39628bb1
commit d0c4264608
Signed by: apio
GPG Key ID: B8A7D06E42258954
8 changed files with 258 additions and 7 deletions

View File

@ -1,4 +1,27 @@
#include <ui/App.h> #include <ui/App.h>
#include <ui/Layout.h>
struct ColorWidget : public ui::Widget
{
public:
ColorWidget(ui::Color color) : m_color(color)
{
}
Result<ui::EventResult> handle_mousemove(ui::Point) override
{
return ui::EventResult::DidNotHandle;
}
Result<void> draw(ui::Canvas& canvas) override
{
canvas.fill(m_color);
return {};
}
private:
ui::Color m_color;
};
Result<int> luna_main(int argc, char** argv) Result<int> luna_main(int argc, char** argv)
{ {
@ -9,13 +32,25 @@ Result<int> luna_main(int argc, char** argv)
app.set_main_window(window); app.set_main_window(window);
window->set_title("Main Window"); window->set_title("Main Window");
window->canvas().fill(ui::CYAN); window->set_background(ui::CYAN);
window->update();
auto* dialog = TRY(ui::Window::create(ui::Rect { 400, 400, 200, 150 })); ui::HorizontalLayout layout;
dialog->set_title("Error: Unknown Error"); window->set_main_widget(layout);
dialog->canvas().fill(ui::RED);
dialog->update(); ColorWidget green(ui::GREEN);
layout.add_widget(green);
ColorWidget blue(ui::BLUE);
layout.add_widget(blue);
ui::HorizontalLayout sublayout;
layout.add_widget(sublayout);
ColorWidget red(ui::RED);
sublayout.add_widget(red);
ColorWidget white(ui::WHITE);
sublayout.add_widget(white);
window->draw();
return app.run(); return app.run();
} }

View File

@ -12,6 +12,7 @@ set(SOURCES
src/Image.cpp src/Image.cpp
src/App.cpp src/App.cpp
src/Window.cpp src/Window.cpp
src/Layout.cpp
) )
add_library(ui ${SOURCES}) add_library(ui ${SOURCES})

45
libui/include/ui/Layout.h Normal file
View File

@ -0,0 +1,45 @@
/**
* @file Layout.h
* @author apio (cloudapio.eu)
* @brief Layout widgets to organize content.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <luna/Vector.h>
#include <ui/Widget.h>
namespace ui
{
enum class AdjustHeight
{
No,
Yes
};
enum class AdjustWidth
{
No,
Yes
};
class HorizontalLayout final : public Widget
{
public:
HorizontalLayout(AdjustHeight adjust_height = AdjustHeight::Yes, AdjustWidth adjust_width = AdjustWidth::Yes);
Result<EventResult> handle_mousemove(Point position) override;
Result<void> draw(Canvas& canvas) override;
Result<void> add_widget(Widget& widget);
private:
Vector<Widget*> m_widgets;
AdjustHeight m_adjust_height;
AdjustWidth m_adjust_width;
int m_used_width;
};
}

65
libui/include/ui/Widget.h Normal file
View File

@ -0,0 +1,65 @@
/**
* @file Widget.h
* @author apio (cloudapio.eu)
* @brief Abstract widget class.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <luna/Result.h>
#include <ui/Canvas.h>
#include <ui/Point.h>
#include <ui/Rect.h>
namespace ui
{
class Window;
enum class EventResult
{
DidHandle,
DidNotHandle,
};
class Widget
{
public:
virtual Result<EventResult> handle_mousemove(Point position);
virtual Result<void> draw(Canvas& canvas);
void set_window(Window* window, Rect rect, Badge<Window>)
{
m_window = window;
m_rect = rect;
}
void set_parent(Widget* parent)
{
m_parent = parent;
m_window = parent->m_window;
}
Widget* parent()
{
return m_parent;
}
Window* window()
{
return m_window;
}
Rect& rect()
{
return m_rect;
}
protected:
Widget* m_parent { nullptr };
Window* m_window;
Rect m_rect { 0, 0, 50, 50 };
};
}

View File

@ -12,6 +12,7 @@
#include <luna/StringView.h> #include <luna/StringView.h>
#include <ui/Canvas.h> #include <ui/Canvas.h>
#include <ui/Rect.h> #include <ui/Rect.h>
#include <ui/Widget.h>
namespace ui namespace ui
{ {
@ -22,6 +23,18 @@ namespace ui
void set_title(StringView title); void set_title(StringView title);
void set_background(Color color)
{
m_background = color;
}
void set_main_widget(Widget& widget)
{
check(!m_main_widget);
widget.set_window(this, m_canvas.rect(), {});
m_main_widget = &widget;
}
Canvas& canvas() Canvas& canvas()
{ {
return m_canvas; return m_canvas;
@ -31,6 +44,9 @@ namespace ui
void close(); void close();
Result<void> draw();
Result<void> handle_mousemove(ui::Point position);
int id() const int id() const
{ {
return m_id; return m_id;
@ -41,5 +57,7 @@ namespace ui
private: private:
int m_id; int m_id;
Canvas m_canvas; Canvas m_canvas;
Widget* m_main_widget { nullptr };
Color m_background { ui::BLACK };
}; };
} }

View File

@ -95,7 +95,9 @@ namespace ui
case MOUSE_EVENT_REQUEST_ID: { case MOUSE_EVENT_REQUEST_ID: {
MouseEventRequest request; MouseEventRequest request;
TRY(m_client->recv_typed(request)); TRY(m_client->recv_typed(request));
os::eprintln("ui: Mouse move to (%d, %d)", request.position.x, request.position.y); auto* window = find_window(request.window);
window->handle_mousemove(request.position);
window->draw();
return {}; return {};
} }
default: fail("Unexpected IPC request from server!"); default: fail("Unexpected IPC request from server!");

72
libui/src/Layout.cpp Normal file
View File

@ -0,0 +1,72 @@
/**
* @file Layout.cpp
* @author apio (cloudapio.eu)
* @brief Layout widgets to organize content.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <stdlib.h>
#include <ui/Layout.h>
namespace ui
{
HorizontalLayout::HorizontalLayout(AdjustHeight adjust_height, AdjustWidth adjust_width)
: m_adjust_height(adjust_height), m_adjust_width(adjust_width)
{
}
Result<EventResult> HorizontalLayout::handle_mousemove(Point position)
{
for (auto widget : m_widgets)
{
if (widget->rect().contains(position)) return widget->handle_mousemove(position);
}
return ui::EventResult::DidNotHandle;
}
Result<void> HorizontalLayout::draw(Canvas& canvas)
{
for (auto widget : m_widgets)
{
ui::Rect rect = { m_rect.relative(widget->rect().pos), widget->rect().width, widget->rect().height };
auto subcanvas = canvas.subcanvas(rect);
TRY(widget->draw(subcanvas));
}
return {};
}
Result<void> HorizontalLayout::add_widget(Widget& widget)
{
TRY(m_widgets.try_append(&widget));
if (m_adjust_width == AdjustWidth::No)
{
widget.rect().pos.x = m_rect.pos.x + m_used_width;
m_used_width += widget.rect().width;
}
else
{
int used_width = 0;
div_t result = div(m_rect.width, (int)m_widgets.size());
for (auto w : m_widgets)
{
w->rect().pos.x = m_rect.pos.x + used_width;
w->rect().width = result.quot;
used_width += result.quot;
}
m_widgets[m_widgets.size() - 1]->rect().width += result.rem;
}
widget.rect().pos.y = m_rect.pos.y;
if (m_adjust_height == AdjustHeight::Yes) { widget.rect().height = m_rect.height; }
widget.set_parent(this);
return {};
}
}

View File

@ -91,4 +91,17 @@ namespace ui
app.unregister_window(this, {}); app.unregister_window(this, {});
} }
Result<void> Window::draw()
{
if (m_main_widget) return m_main_widget->draw(m_canvas);
update();
return {};
}
Result<void> Window::handle_mousemove(ui::Point position)
{
if (m_main_widget) TRY(m_main_widget->handle_mousemove(position));
return {};
}
} }