Compare commits

...

2 Commits

Author SHA1 Message Date
4087c7510e
libui: Fix wrong return type
All checks were successful
Build and test / build (push) Successful in 2m26s
2024-06-08 22:18:42 +02:00
280b0c90af
apps+editor+libui+terminal: Rework the layout system
Some checks failed
Build and test / build (push) Failing after 1m49s
2024-05-19 14:21:30 +02:00
26 changed files with 453 additions and 130 deletions

View File

@ -23,6 +23,16 @@ struct Tile
class GameWidget final : public ui::Widget class GameWidget final : public ui::Widget
{ {
public: public:
GameWidget(ui::Window* window, ui::Widget* parent) : ui::Widget(window, parent)
{
}
void show_tree(int indent) override
{
os::println("%*s- 2048 GameWidget (%d,%d,%d,%d)", indent, "", m_rect.pos.x, m_rect.pos.y, m_rect.width,
m_rect.height);
}
static constexpr int MARGIN = 5; static constexpr int MARGIN = 5;
Result<void> draw(ui::Canvas& canvas) override Result<void> draw(ui::Canvas& canvas) override
@ -356,9 +366,8 @@ Result<int> luna_main(int, char**)
window->set_background(ui::BLACK); window->set_background(ui::BLACK);
window->set_title("2048"); window->set_title("2048");
GameWidget game; auto* game = TRY(window->create_main_widget<GameWidget>());
window->set_main_widget(game); game->reset();
game.reset();
return app.run(); return app.run();
} }

View File

@ -21,27 +21,26 @@ Result<int> luna_main(int, char**)
utsname info; utsname info;
uname(&info); uname(&info);
ui::VerticalLayout main_layout; auto* main_layout = TRY(window->create_main_widget<ui::VerticalLayout>());
window->set_main_widget(main_layout); main_layout->set_layout_settings(ui::Margins { 0, 0, 20, 40 });
ui::Label title("About Luna"); auto* title = TRY(main_layout->add_child_widget<ui::Label>());
title.set_font(ui::Font::default_bold_font()); title->set_text("About Luna");
title->set_font(ui::Font::default_bold_font());
main_layout.add_widget(title); auto* version_info = TRY(main_layout->add_child_widget<ui::VerticalLayout>());
version_info->set_layout_settings(ui::Margins { 0, 0, 10, 10 });
ui::VerticalLayout version_info;
main_layout.add_widget(version_info);
ui::Label license("Licensed under the BSD-2-Clause license.");
main_layout.add_widget(license);
auto* os_release = TRY(version_info->add_child_widget<ui::Label>());
String os_release_text = TRY(String::format("OS release: %s"_sv, info.release)); String os_release_text = TRY(String::format("OS release: %s"_sv, info.release));
ui::Label os_release(os_release_text.view()); os_release->set_text(os_release_text.view());
version_info.add_widget(os_release);
auto* kernel_version = TRY(version_info->add_child_widget<ui::Label>());
String kernel_version_text = TRY(String::format("Kernel version: %s"_sv, info.version)); String kernel_version_text = TRY(String::format("Kernel version: %s"_sv, info.version));
ui::Label kernel_version(kernel_version_text.view()); kernel_version->set_text(kernel_version_text.view());
version_info.add_widget(kernel_version);
auto* license = TRY(version_info->add_child_widget<ui::Label>());
license->set_text("Licensed under the BSD-2-Clause license.");
return app.run(); return app.run();
} }

View File

@ -29,12 +29,12 @@ Result<int> luna_main(int, char**)
window->set_title("Clock"); window->set_title("Clock");
window->set_background(ui::GRAY); window->set_background(ui::GRAY);
g_label = TRY(make<ui::Label>("00:00:00")); g_label = TRY(window->create_main_widget<ui::Label>());
g_label->set_text("00:00:00");
g_label->set_font(ui::Font::default_bold_font()); g_label->set_font(ui::Font::default_bold_font());
g_label->set_color(ui::BLACK); g_label->set_color(ui::BLACK);
window->set_main_widget(*g_label);
update_time(); update_time();
auto timer = TRY(os::Timer::create_repeating(1000, update_time)); auto timer = TRY(os::Timer::create_repeating(1000, update_time));

View File

@ -30,24 +30,19 @@ void sighup_handler(int)
os::Process::exec(args[0], { args, 1 }); os::Process::exec(args[0], { args, 1 });
} }
Result<void> create_widget_group_for_app(ui::HorizontalLayout& layout, StringView path, StringView icon) Result<void> create_widget_group_for_app(ui::HorizontalLayout* layout, StringView path, StringView icon)
{ {
auto* button = TRY(make<ui::Button>(ui::Rect { 0, 0, 50, 50 })); auto* button = TRY(layout->add_child_widget<ui::Button>());
layout.add_widget(*button);
auto* container = TRY( auto* container = TRY(button->create_child_widget<ui::Container>());
make<ui::Container>(ui::Rect { 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center));
button->set_widget(*container);
button->set_action([=] { button->set_action([=] {
os::Launcher::LaunchDetachedRequest request; os::Launcher::LaunchDetachedRequest request;
SET_IPC_STRING(request.command, path.chars()); SET_IPC_STRING(request.command, path.chars());
launcher_client->send_async(request); launcher_client->send_async(request);
}); });
auto image = TRY(ui::ImageWidget::load(icon)); auto image = TRY(container->create_child_widget<ui::ImageWidget>());
container->set_widget(*image); image->load(icon);
image.leak();
return {}; return {};
} }
@ -165,8 +160,8 @@ Result<int> luna_main(int, char**)
window->set_background(TASKBAR_COLOR); window->set_background(TASKBAR_COLOR);
window->set_special_attributes(ui::UNFOCUSEABLE); window->set_special_attributes(ui::UNFOCUSEABLE);
ui::HorizontalLayout layout(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No); auto* layout = TRY(window->create_main_widget<ui::HorizontalLayout>());
window->set_main_widget(layout); layout->set_layout_settings(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No);
load_app_files_from_path("/usr/share/applications/"); load_app_files_from_path("/usr/share/applications/");

View File

@ -15,7 +15,8 @@
#include <os/FileSystem.h> #include <os/FileSystem.h>
#include <ui/App.h> #include <ui/App.h>
EditorWidget::EditorWidget(SharedPtr<ui::Font> font) : ui::TextInput(), m_font(font) EditorWidget::EditorWidget(ui::Window* window, ui::Widget* parent)
: ui::TextInput(window, parent), m_font(ui::Font::default_font())
{ {
recalculate_lines(); recalculate_lines();
} }

View File

@ -16,7 +16,7 @@
class EditorWidget : public ui::TextInput class EditorWidget : public ui::TextInput
{ {
public: public:
EditorWidget(SharedPtr<ui::Font> font); EditorWidget(ui::Window* window, ui::Widget* parent);
Result<void> load_file(const os::Path& path); Result<void> load_file(const os::Path& path);
@ -31,6 +31,11 @@ class EditorWidget : public ui::TextInput
return m_path; return m_path;
} }
void set_font(SharedPtr<ui::Font> font)
{
m_font = font;
}
private: private:
SharedPtr<ui::Font> m_font; SharedPtr<ui::Font> m_font;

View File

@ -30,8 +30,7 @@ Result<int> luna_main(int argc, char** argv)
window->set_title("Text Editor"); window->set_title("Text Editor");
app.set_main_window(window); app.set_main_window(window);
auto* editor = TRY(make<EditorWidget>(ui::Font::default_font())); auto* editor = TRY(window->create_main_widget<EditorWidget>());
window->set_main_widget(*editor);
if (!path.is_empty()) TRY(editor->load_file(path)); if (!path.is_empty()) TRY(editor->load_file(path));
TRY(window->add_keyboard_shortcut({ moon::K_CH26, ui::Mod_Ctrl }, true, [&](ui::Shortcut) { TRY(window->add_keyboard_shortcut({ moon::K_CH26, ui::Mod_Ctrl }, true, [&](ui::Shortcut) {
@ -41,7 +40,5 @@ Result<int> luna_main(int argc, char** argv)
os::println("editor: buffer saved to %s successfully", editor->path().name().chars()); os::println("editor: buffer saved to %s successfully", editor->path().name().chars());
})); }));
window->draw();
return app.run(); return app.run();
} }

View File

@ -9,16 +9,25 @@
#pragma once #pragma once
#include <os/Action.h> #include <os/Action.h>
#include <os/File.h>
#include <ui/Widget.h> #include <ui/Widget.h>
#include <ui/Window.h>
namespace ui namespace ui
{ {
class Button : public Widget class Button : public Widget
{ {
public: public:
Button(Rect rect); Button(ui::Window* window, ui::Widget* parent);
template <typename T> Result<T*> create_child_widget()
{
auto* widget = TRY(make<T>(window(), this));
m_child = widget;
window()->recalculate_layout();
return widget;
}
void set_widget(Widget& widget);
void set_action(os::Action&& action); void set_action(os::Action&& action);
Result<EventResult> handle_mouse_move(Point position) override; Result<EventResult> handle_mouse_move(Point position) override;
@ -28,10 +37,20 @@ namespace ui
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override; Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;
void recalculate_true_rects() override;
void recalculate_pseudo_rects() override;
void show_tree(int indent) override
{
os::println("%*s- ui::Button (%d,%d,%d,%d)", indent, "", m_rect.pos.x, m_rect.pos.y, m_rect.width,
m_rect.height);
if (m_child) m_child->show_tree(indent + 1);
}
private: private:
bool m_hovered { false }; bool m_hovered { false };
bool m_clicked { false }; bool m_clicked { false };
Widget* m_child; Widget* m_child { nullptr };
os::Action m_action; os::Action m_action;
}; };
} }

View File

@ -8,17 +8,31 @@
*/ */
#pragma once #pragma once
#include <os/File.h>
#include <ui/Alignment.h> #include <ui/Alignment.h>
#include <ui/Widget.h> #include <ui/Widget.h>
#include <ui/Window.h>
namespace ui namespace ui
{ {
class Container : public Widget class Container : public Widget
{ {
public: public:
Container(Rect rect, VerticalAlignment valign, HorizontalAlignment halign); Container(ui::Window* window, ui::Widget* parent);
void set_widget(Widget& widget); void set_container_settings(VerticalAlignment valign, HorizontalAlignment halign)
{
m_valign = valign;
m_halign = halign;
}
template <typename T> Result<T*> create_child_widget()
{
auto* widget = TRY(make<T>(window(), this));
m_widget = widget;
window()->recalculate_layout();
return widget;
}
Result<EventResult> handle_mouse_move(Point position) override; Result<EventResult> handle_mouse_move(Point position) override;
Result<EventResult> handle_mouse_leave() override; Result<EventResult> handle_mouse_leave() override;
@ -27,9 +41,19 @@ namespace ui
Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override; Result<EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;
void recalculate_true_rects() override;
void recalculate_pseudo_rects() override;
void show_tree(int indent) override
{
os::println("%*s- ui::Container (%d,%d,%d,%d)", indent, "", m_rect.pos.x, m_rect.pos.y, m_rect.width,
m_rect.height);
if (m_widget) m_widget->show_tree(indent + 1);
}
private: private:
Widget* m_widget; Widget* m_widget { nullptr };
VerticalAlignment m_valign; VerticalAlignment m_valign = VerticalAlignment::Center;
HorizontalAlignment m_halign; HorizontalAlignment m_halign = HorizontalAlignment::Center;
}; };
} }

View File

@ -10,6 +10,7 @@
#pragma once #pragma once
#include <luna/Buffer.h> #include <luna/Buffer.h>
#include <luna/SharedPtr.h> #include <luna/SharedPtr.h>
#include <os/File.h>
#include <os/Path.h> #include <os/Path.h>
#include <ui/Widget.h> #include <ui/Widget.h>
@ -82,10 +83,20 @@ namespace ui
class ImageWidget final : public Widget class ImageWidget final : public Widget
{ {
public: public:
static Result<OwnedPtr<ImageWidget>> load(const os::Path& path); ImageWidget(ui::Window* window, ui::Widget* parent);
Result<void> load(const os::Path& path);
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;
void recalculate_pseudo_rects() override;
void show_tree(int indent) override
{
os::println("%*s- ui::Image %dx%d (%d,%d,%d,%d)", indent, "", m_image->width(), m_image->height(),
m_rect.pos.x, m_rect.pos.y, m_rect.width, m_rect.height);
}
private: private:
SharedPtr<Image> m_image; SharedPtr<Image> m_image;
}; };

View File

@ -17,7 +17,12 @@ namespace ui
class InputField : public ui::TextInput class InputField : public ui::TextInput
{ {
public: public:
InputField(SharedPtr<ui::Font> font); InputField(Window* window, Widget* parent);
void set_font(SharedPtr<ui::Font> font)
{
m_font = font;
}
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override; Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override;

View File

@ -8,6 +8,7 @@
*/ */
#pragma once #pragma once
#include <os/File.h>
#include <ui/Alignment.h> #include <ui/Alignment.h>
#include <ui/Font.h> #include <ui/Font.h>
#include <ui/Widget.h> #include <ui/Widget.h>
@ -22,7 +23,7 @@ namespace ui
class Label final : public Widget class Label final : public Widget
{ {
public: public:
Label(StringView text); Label(Window* window, Widget* parent);
void set_alignment(VerticalAlignment valign, HorizontalAlignment halign) void set_alignment(VerticalAlignment valign, HorizontalAlignment halign)
{ {
@ -47,6 +48,14 @@ namespace ui
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;
void recalculate_pseudo_rects() override;
void show_tree(int indent) override
{
os::println("%*s- ui::Label '%s' (%d,%d,%d,%d)", indent, "", m_text.chars(), m_rect.pos.x, m_rect.pos.y,
m_rect.width, m_rect.height);
}
private: private:
StringView m_text; StringView m_text;
VerticalAlignment m_valign = VerticalAlignment::Center; VerticalAlignment m_valign = VerticalAlignment::Center;

View File

@ -9,6 +9,7 @@
#pragma once #pragma once
#include <luna/Vector.h> #include <luna/Vector.h>
#include <os/File.h>
#include <ui/Widget.h> #include <ui/Widget.h>
namespace ui namespace ui
@ -36,8 +37,16 @@ namespace ui
class HorizontalLayout final : public Widget class HorizontalLayout final : public Widget
{ {
public: public:
HorizontalLayout(Margins margins = Margins { 0, 0, 0, 0 }, AdjustHeight adjust_height = AdjustHeight::Yes, HorizontalLayout(ui::Window* window, ui::Widget* parent);
AdjustWidth adjust_width = AdjustWidth::Yes);
void set_layout_settings(Margins margins = Margins { 0, 0, 0, 0 },
AdjustHeight adjust_height = AdjustHeight::Yes,
AdjustWidth adjust_width = AdjustWidth::No)
{
m_margins = margins;
m_adjust_height = adjust_height;
m_adjust_width = adjust_width;
}
Result<EventResult> handle_mouse_move(Point position) override; Result<EventResult> handle_mouse_move(Point position) override;
Result<EventResult> handle_mouse_leave() override; Result<EventResult> handle_mouse_leave() override;
@ -47,21 +56,46 @@ namespace ui
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;
Result<void> add_widget(Widget& widget); template <typename T> Result<T*> add_child_widget()
{
auto* widget = TRY(make<T>(window(), this));
add_widget(*widget);
return widget;
}
void recalculate_pseudo_rects() override;
void recalculate_true_rects() override;
void show_tree(int indent) override
{
os::println("%*s- ui::HorizontalLayout [%zu widgets] (%d,%d,%d,%d)", indent, "", m_widgets.size(),
m_rect.pos.x, m_rect.pos.y, m_rect.width, m_rect.height);
for (auto* w : m_widgets) { w->show_tree(indent + 1); }
}
private: private:
Vector<Widget*> m_widgets; Vector<Widget*> m_widgets;
Margins m_margins; Margins m_margins { 0, 0, 0, 0 };
AdjustHeight m_adjust_height; AdjustHeight m_adjust_height = AdjustHeight::Yes;
AdjustWidth m_adjust_width; AdjustWidth m_adjust_width = AdjustWidth::No;
int m_used_width { 0 }; int m_used_width { 0 };
Result<void> add_widget(Widget& widget);
}; };
class VerticalLayout final : public Widget class VerticalLayout final : public Widget
{ {
public: public:
VerticalLayout(Margins margins = Margins { 0, 0, 0, 0 }, AdjustHeight adjust_height = AdjustHeight::Yes, VerticalLayout(ui::Window* window, ui::Widget* parent);
AdjustWidth adjust_width = AdjustWidth::Yes);
void set_layout_settings(Margins margins = Margins { 0, 0, 0, 0 },
AdjustHeight adjust_height = AdjustHeight::No,
AdjustWidth adjust_width = AdjustWidth::Yes)
{
m_margins = margins;
m_adjust_height = adjust_height;
m_adjust_width = adjust_width;
}
Result<EventResult> handle_mouse_move(Point position) override; Result<EventResult> handle_mouse_move(Point position) override;
Result<EventResult> handle_mouse_leave() override; Result<EventResult> handle_mouse_leave() override;
@ -71,13 +105,30 @@ namespace ui
Result<void> draw(Canvas& canvas) override; Result<void> draw(Canvas& canvas) override;
Result<void> add_widget(Widget& widget); template <typename T> Result<T*> add_child_widget()
{
auto* widget = TRY(make<T>(window(), this));
add_widget(*widget);
return widget;
}
void recalculate_pseudo_rects() override;
void recalculate_true_rects() override;
void show_tree(int indent) override
{
os::println("%*s- ui::VerticalLayout [%zu widgets] (%d,%d,%d,%d)", indent, "", m_widgets.size(),
m_rect.pos.x, m_rect.pos.y, m_rect.width, m_rect.height);
for (auto* w : m_widgets) { w->show_tree(indent + 1); }
}
private: private:
Vector<Widget*> m_widgets; Vector<Widget*> m_widgets;
Margins m_margins; Margins m_margins { 0, 0, 0, 0 };
AdjustHeight m_adjust_height; AdjustHeight m_adjust_height = AdjustHeight::No;
AdjustWidth m_adjust_width; AdjustWidth m_adjust_width = AdjustWidth::Yes;
int m_used_height { 0 }; int m_used_height { 0 };
Result<void> add_widget(Widget& widget);
}; };
} }

View File

@ -9,6 +9,7 @@
#pragma once #pragma once
#include <luna/Buffer.h> #include <luna/Buffer.h>
#include <os/File.h>
#include <os/Timer.h> #include <os/Timer.h>
#include <ui/Widget.h> #include <ui/Widget.h>
@ -17,12 +18,18 @@ namespace ui
class TextInput : public Widget class TextInput : public Widget
{ {
public: public:
TextInput(); TextInput(Window* window, Widget* parent);
virtual Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) = 0; virtual Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) = 0;
virtual Result<void> draw(ui::Canvas& canvas) = 0; virtual Result<void> draw(ui::Canvas& canvas) = 0;
void show_tree(int indent) override
{
os::println("%*s- ui::TextInput (%d,%d,%d,%d)", indent, "", m_rect.pos.x, m_rect.pos.y, m_rect.width,
m_rect.height);
}
protected: protected:
Buffer m_data; Buffer m_data;

View File

@ -28,6 +28,10 @@ namespace ui
class Widget class Widget
{ {
public: public:
Widget(Window* window, Widget* parent) : m_window(window), m_parent(parent)
{
}
virtual Result<EventResult> handle_mouse_move(Point position) virtual Result<EventResult> handle_mouse_move(Point position)
{ {
ignore(position); ignore(position);
@ -59,16 +63,14 @@ namespace ui
virtual Result<void> draw(Canvas& canvas); virtual Result<void> draw(Canvas& canvas);
void set_window(Window* window, Rect rect, Badge<Window>) void set_rect(Rect rect, Badge<Window>)
{ {
m_window = window;
m_rect = rect; m_rect = rect;
} }
void set_parent(Widget* parent) void resize(Rect rect)
{ {
m_parent = parent; m_preferred_rect = rect;
m_window = parent->m_window;
} }
Widget* parent() Widget* parent()
@ -86,9 +88,36 @@ namespace ui
return m_rect; return m_rect;
} }
Rect pseudo_rect()
{
return m_pseudo_rect;
}
Rect preferred_rect()
{
return m_preferred_rect.value_or({ 0, 0, 50, 50 });
}
virtual void recalculate_pseudo_rects()
{
m_pseudo_rect = preferred_rect();
return;
}
virtual void recalculate_true_rects()
{
return;
}
virtual void show_tree(int indent) = 0;
protected: protected:
Widget* m_parent { nullptr };
Window* m_window; Window* m_window;
Widget* m_parent { nullptr };
Option<Rect> m_preferred_rect;
Rect m_pseudo_rect;
Rect m_rect { 0, 0, 50, 50 }; Rect m_rect { 0, 0, 50, 50 };
}; };
} }

View File

@ -8,9 +8,12 @@
*/ */
#pragma once #pragma once
#include <luna/Alloc.h>
#include <luna/HashMap.h>
#include <luna/OwnedPtr.h> #include <luna/OwnedPtr.h>
#include <luna/String.h> #include <luna/String.h>
#include <luna/StringView.h> #include <luna/StringView.h>
#include <os/File.h>
#include <ui/Canvas.h> #include <ui/Canvas.h>
#include <ui/Mouse.h> #include <ui/Mouse.h>
#include <ui/Rect.h> #include <ui/Rect.h>
@ -49,11 +52,13 @@ namespace ui
m_background = color; m_background = color;
} }
void set_main_widget(Widget& widget) template <typename T> Result<T*> create_main_widget()
{ {
check(!m_main_widget); check(!m_main_widget);
widget.set_window(this, m_window_canvas.rect(), {}); auto* widget = TRY(make<T>(this, nullptr));
m_main_widget = &widget; m_main_widget = widget;
widget->set_rect(m_window_canvas.rect(), {});
return widget;
} }
Canvas& canvas() Canvas& canvas()
@ -75,11 +80,25 @@ namespace ui
Result<void> add_keyboard_shortcut(ui::Shortcut shortcut, bool intercept, os::Function<ui::Shortcut>&& action); Result<void> add_keyboard_shortcut(ui::Shortcut shortcut, bool intercept, os::Function<ui::Shortcut>&& action);
void recalculate_layout()
{
check(m_main_widget);
m_main_widget->recalculate_pseudo_rects();
m_main_widget->recalculate_true_rects();
}
int id() const int id() const
{ {
return m_id; return m_id;
} }
void show_tree()
{
os::println("- ui::Window %d (%d,%d,%d,%d)", m_id, m_canvas.rect().pos.x, m_canvas.rect().pos.y,
m_canvas.rect().width, m_canvas.rect().height);
m_main_widget->show_tree(1);
}
~Window(); ~Window();
private: private:

View File

@ -12,16 +12,26 @@
namespace ui namespace ui
{ {
Button::Button(Rect rect) Button::Button(Window* window, Widget* parent) : ui::Widget(window, parent)
{ {
m_rect = rect;
} }
void Button::set_widget(Widget& widget) void Button::recalculate_true_rects()
{ {
widget.rect() = m_rect; if (!m_child) return;
m_child = &widget; m_child->rect() = m_rect;
widget.set_parent(this); m_child->recalculate_true_rects();
}
void Button::recalculate_pseudo_rects()
{
if (m_child)
{
m_child->recalculate_pseudo_rects();
m_pseudo_rect = m_child->pseudo_rect();
}
if (m_preferred_rect.has_value()) m_pseudo_rect = m_preferred_rect.value();
} }
void Button::set_action(os::Action&& action) void Button::set_action(os::Action&& action)

View File

@ -11,17 +11,22 @@
namespace ui namespace ui
{ {
Container::Container(Rect rect, VerticalAlignment valign, HorizontalAlignment halign) Container::Container(Window* window, Widget* parent) : ui::Widget(window, parent)
: m_valign(valign), m_halign(halign)
{ {
m_rect = rect;
} }
void Container::set_widget(Widget& widget) void Container::recalculate_true_rects()
{ {
m_widget = &widget; if (!m_widget) return;
widget.rect() = ui::align(m_rect, widget.rect(), m_valign, m_halign); m_widget->rect() = ui::align(m_rect, m_widget->pseudo_rect(), m_valign, m_halign);
widget.set_parent(this); m_widget->recalculate_true_rects();
}
void Container::recalculate_pseudo_rects()
{
if (m_widget) m_widget->recalculate_pseudo_rects();
m_pseudo_rect = preferred_rect();
} }
Result<EventResult> Container::handle_mouse_move(Point position) Result<EventResult> Container::handle_mouse_move(Point position)

View File

@ -10,6 +10,7 @@
#include <os/File.h> #include <os/File.h>
#include <ui/Alignment.h> #include <ui/Alignment.h>
#include <ui/Image.h> #include <ui/Image.h>
#include <ui/Window.h>
namespace ui namespace ui
{ {
@ -32,17 +33,28 @@ namespace ui
return image; return image;
} }
Result<OwnedPtr<ImageWidget>> ImageWidget::load(const os::Path& path) ImageWidget::ImageWidget(Window* window, Widget* parent) : ui::Widget(window, parent)
{ {
auto widget = TRY(make_owned<ImageWidget>()); }
widget->m_image = TRY(Image::load(path));
widget->m_rect = { 0, 0, widget->m_image->width(), widget->m_image->height() }; Result<void> ImageWidget::load(const os::Path& path)
return widget; {
m_image = TRY(Image::load(path));
window()->recalculate_layout();
return {};
} }
Result<void> ImageWidget::draw(Canvas& canvas) Result<void> ImageWidget::draw(Canvas& canvas)
{ {
if (!m_image) return {};
canvas.subcanvas({ 0, 0, m_image->width(), m_image->height() }).fill(m_image->pixels(), m_image->width()); canvas.subcanvas({ 0, 0, m_image->width(), m_image->height() }).fill(m_image->pixels(), m_image->width());
return {}; return {};
} }
void ImageWidget::recalculate_pseudo_rects()
{
if (m_preferred_rect.has_value()) m_pseudo_rect = m_preferred_rect.value();
if (m_image) m_pseudo_rect = ui::Rect { 0, 0, m_image->width(), m_image->height() };
}
} }

View File

@ -15,7 +15,8 @@
namespace ui namespace ui
{ {
InputField::InputField(SharedPtr<ui::Font> font) : ui::TextInput(), m_font(font) InputField::InputField(Window* window, Widget* parent)
: ui::TextInput(window, parent), m_font(ui::Font::default_font())
{ {
} }

View File

@ -12,7 +12,7 @@
namespace ui namespace ui
{ {
Label::Label(StringView text) : m_text(text) Label::Label(Window* window, Widget* parent) : Widget(window, parent), m_text()
{ {
m_font = ui::Font::default_font(); m_font = ui::Font::default_font();
} }
@ -33,4 +33,14 @@ namespace ui
m_font->render(buf, m_color, subcanvas); m_font->render(buf, m_color, subcanvas);
return {}; return {};
} }
void Label::recalculate_pseudo_rects()
{
if (m_preferred_rect.has_value()) m_pseudo_rect = m_preferred_rect.value();
int height = m_font->height();
int width = m_font->width() * (int)m_text.length();
m_pseudo_rect = { 0, 0, width, height };
}
} }

View File

@ -9,11 +9,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <ui/Layout.h> #include <ui/Layout.h>
#include <ui/Window.h>
namespace ui namespace ui
{ {
HorizontalLayout::HorizontalLayout(Margins margins, AdjustHeight adjust_height, AdjustWidth adjust_width) HorizontalLayout::HorizontalLayout(Window* window, Widget* parent) : ui::Widget(window, parent)
: m_margins(margins), m_adjust_height(adjust_height), m_adjust_width(adjust_width)
{ {
} }
@ -93,38 +93,85 @@ namespace ui
{ {
TRY(m_widgets.try_append(&widget)); TRY(m_widgets.try_append(&widget));
if (m_adjust_width == AdjustWidth::No) window()->recalculate_layout();
return {};
}
void HorizontalLayout::recalculate_pseudo_rects()
{
int min_height = 0;
int width = 0;
for (auto w : m_widgets)
{ {
widget.rect().pos.x = m_rect.pos.x + m_used_width + m_margins.left; w->recalculate_pseudo_rects();
m_used_width += m_margins.left + widget.rect().width + m_margins.right; if (w->pseudo_rect().height > min_height) min_height = w->pseudo_rect().height;
width += (m_margins.left + w->pseudo_rect().width + m_margins.right);
} }
else
min_height += (m_margins.top + m_margins.bottom);
m_pseudo_rect = { 0, 0, width, min_height };
if (m_preferred_rect.has_value()) m_pseudo_rect = m_preferred_rect.value();
}
void HorizontalLayout::recalculate_true_rects()
{
if (m_adjust_width == AdjustWidth::Yes)
{ {
int used_width = 0; int used_width = 0;
div_t result = div(m_rect.width, (int)m_widgets.size()); div_t result = div(m_rect.width, (int)m_widgets.size());
for (auto w : m_widgets) for (auto w : m_widgets)
{ {
w->rect().pos.x = m_rect.pos.x + used_width + m_margins.left; w->rect().pos.x = m_rect.pos.x + used_width + m_margins.left;
w->rect().pos.y = m_rect.pos.y + m_margins.top;
w->rect().width = result.quot - (m_margins.left + m_margins.right); w->rect().width = result.quot - (m_margins.left + m_margins.right);
if (m_adjust_height == AdjustHeight::Yes)
{
w->rect().height = m_rect.height - (m_margins.top + m_margins.bottom);
}
else
w->rect().height = w->pseudo_rect().height;
used_width += result.quot; used_width += result.quot;
w->recalculate_true_rects();
}
if (result.rem != 0 && m_widgets.size())
{
auto* last = m_widgets[m_widgets.size() - 1];
last->rect().width += result.rem;
last->recalculate_true_rects();
} }
m_widgets[m_widgets.size() - 1]->rect().width += result.rem;
} }
else
widget.rect().pos.y = m_rect.pos.y + m_margins.top;
if (m_adjust_height == AdjustHeight::Yes)
{ {
widget.rect().height = m_rect.height - (m_margins.top + m_margins.bottom); int used_width = 0;
for (auto w : m_widgets)
{
w->rect() = w->pseudo_rect();
w->rect().pos.x = m_rect.pos.x + used_width + m_margins.left;
w->rect().pos.y = m_rect.pos.y + m_margins.top;
if (m_adjust_height == AdjustHeight::Yes)
{
w->rect().height = m_rect.height - (m_margins.top + m_margins.bottom);
}
else
w->rect().height = w->pseudo_rect().height;
used_width += (m_margins.left + m_margins.right + w->rect().width);
w->recalculate_true_rects();
}
} }
widget.set_parent(this);
return {};
} }
VerticalLayout::VerticalLayout(Margins margins, AdjustHeight adjust_height, AdjustWidth adjust_width) VerticalLayout::VerticalLayout(Window* window, Widget* parent) : ui::Widget(window, parent)
: m_margins(margins), m_adjust_height(adjust_height), m_adjust_width(adjust_width)
{ {
} }
@ -200,36 +247,84 @@ namespace ui
return {}; return {};
} }
Result<void> VerticalLayout::add_widget(Widget& widget) void VerticalLayout::recalculate_pseudo_rects()
{ {
TRY(m_widgets.try_append(&widget)); int min_width = 0;
int height = 0;
if (m_adjust_height == AdjustHeight::No) for (auto w : m_widgets)
{ {
widget.rect().pos.y = m_rect.pos.y + m_used_height + m_margins.top; w->recalculate_pseudo_rects();
m_used_height += m_margins.top + widget.rect().height + m_margins.bottom; if (w->pseudo_rect().width > min_width) min_width = w->pseudo_rect().width;
height += (m_margins.top + w->pseudo_rect().height + m_margins.bottom);
} }
else
min_width += (m_margins.left + m_margins.right);
m_pseudo_rect = { 0, 0, min_width, height };
if (m_preferred_rect.has_value()) m_pseudo_rect = m_preferred_rect.value();
}
void VerticalLayout::recalculate_true_rects()
{
if (m_adjust_height == AdjustHeight::Yes)
{ {
int used_height = 0; int used_height = 0;
div_t result = div(m_rect.height, (int)m_widgets.size()); div_t result = div(m_rect.height, (int)m_widgets.size());
for (auto w : m_widgets) for (auto w : m_widgets)
{ {
w->rect().pos.y = m_rect.pos.y + used_height + m_margins.top; w->rect().pos.y = m_rect.pos.y + used_height + m_margins.top;
w->rect().pos.x = m_rect.pos.x + m_margins.left;
w->rect().height = result.quot - (m_margins.top + m_margins.bottom); w->rect().height = result.quot - (m_margins.top + m_margins.bottom);
if (m_adjust_width == AdjustWidth::Yes)
{
w->rect().width = m_rect.width - (m_margins.left + m_margins.right);
}
else
w->rect().width = w->pseudo_rect().width;
used_height += result.quot; used_height += result.quot;
w->recalculate_true_rects();
}
if (result.rem != 0 && m_widgets.size())
{
auto* last = m_widgets[m_widgets.size() - 1];
last->rect().height += result.rem;
last->recalculate_true_rects();
} }
m_widgets[m_widgets.size() - 1]->rect().height += result.rem;
} }
else
widget.rect().pos.x = m_rect.pos.x + m_margins.left;
if (m_adjust_width == AdjustWidth::Yes)
{ {
widget.rect().width = m_rect.width - (m_margins.left + m_margins.right); int used_height = 0;
} for (auto w : m_widgets)
{
w->rect() = w->pseudo_rect();
w->rect().pos.y = m_rect.pos.y + used_height + m_margins.top;
w->rect().pos.x = m_rect.pos.x + m_margins.left;
widget.set_parent(this); if (m_adjust_width == AdjustWidth::Yes)
{
w->rect().width = m_rect.width - (m_margins.left + m_margins.right);
}
else
w->rect().width = w->pseudo_rect().width;
used_height += (m_margins.top + m_margins.bottom + w->rect().height);
w->recalculate_true_rects();
}
}
}
Result<void> VerticalLayout::add_widget(Widget& widget)
{
TRY(m_widgets.try_append(&widget));
window()->recalculate_layout();
return {}; return {};
} }

View File

@ -12,7 +12,7 @@
namespace ui namespace ui
{ {
TextInput::TextInput() : Widget() TextInput::TextInput(Window* window, Widget* parent) : Widget(window, parent)
{ {
m_cursor_timer = os::Timer::create_repeating(500, [this]() { this->tick_cursor(); }).release_value(); m_cursor_timer = os::Timer::create_repeating(500, [this]() { this->tick_cursor(); }).release_value();
} }

View File

@ -35,6 +35,10 @@ static void sigchld_handler(int)
ui::App::the().set_should_close(true); ui::App::the().set_should_close(true);
} }
TerminalWidget::TerminalWidget(ui::Window* window, ui::Widget* parent) : ui::Widget(window, parent)
{
}
Result<void> TerminalWidget::init(char* const* args) Result<void> TerminalWidget::init(char* const* args)
{ {
m_font = ui::Font::default_font(); m_font = ui::Font::default_font();

View File

@ -2,6 +2,7 @@
#include <luna/EscapeSequence.h> #include <luna/EscapeSequence.h>
#include <luna/Utf8.h> #include <luna/Utf8.h>
#include <luna/Vector.h> #include <luna/Vector.h>
#include <os/File.h>
#include <os/Timer.h> #include <os/Timer.h>
#include <stdio.h> #include <stdio.h>
#include <termios.h> #include <termios.h>
@ -11,12 +12,20 @@
class TerminalWidget : public ui::Widget class TerminalWidget : public ui::Widget
{ {
public: public:
TerminalWidget(ui::Window* window, ui::Widget* parent);
Result<void> init(char* const* args); Result<void> init(char* const* args);
Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override; Result<ui::EventResult> handle_key_event(const ui::KeyEventRequest& request) override;
Result<void> draw(ui::Canvas& canvas) override; Result<void> draw(ui::Canvas& canvas) override;
void show_tree(int indent) override
{
os::println("%*s- TerminalWidget (%d,%d,%d,%d)", indent, "", m_rect.pos.x, m_rect.pos.y, m_rect.width,
m_rect.height);
}
private: private:
ui::Canvas m_terminal_canvas; ui::Canvas m_terminal_canvas;
Vector<u8> m_line_buffer; Vector<u8> m_line_buffer;

View File

@ -12,13 +12,10 @@ Result<int> luna_main(int, char**)
app.set_main_window(window); app.set_main_window(window);
window->set_title("Terminal"); window->set_title("Terminal");
TerminalWidget terminal; auto* terminal = TRY(window->create_main_widget<TerminalWidget>());
window->set_main_widget(terminal);
char* args[] = { "/bin/sh", nullptr }; char* args[] = { "/bin/sh", nullptr };
TRY(terminal.init(args)); TRY(terminal->init(args));
window->draw();
return app.run(); return app.run();
} }