diff --git a/apps/2048.cpp b/apps/2048.cpp index 039981f0..f4f6ca7e 100644 --- a/apps/2048.cpp +++ b/apps/2048.cpp @@ -23,6 +23,16 @@ struct Tile class GameWidget final : public ui::Widget { 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; Result draw(ui::Canvas& canvas) override @@ -356,9 +366,8 @@ Result luna_main(int, char**) window->set_background(ui::BLACK); window->set_title("2048"); - GameWidget game; - window->set_main_widget(game); - game.reset(); + auto* game = TRY(window->create_main_widget()); + game->reset(); return app.run(); } diff --git a/apps/about.cpp b/apps/about.cpp index c2e2a06e..7580e503 100644 --- a/apps/about.cpp +++ b/apps/about.cpp @@ -21,27 +21,26 @@ Result luna_main(int, char**) utsname info; uname(&info); - ui::VerticalLayout main_layout; - window->set_main_widget(main_layout); + auto* main_layout = TRY(window->create_main_widget()); + main_layout->set_layout_settings(ui::Margins { 0, 0, 20, 40 }); - ui::Label title("About Luna"); - title.set_font(ui::Font::default_bold_font()); + auto* title = TRY(main_layout->add_child_widget()); + title->set_text("About Luna"); + title->set_font(ui::Font::default_bold_font()); - main_layout.add_widget(title); - - 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* version_info = TRY(main_layout->add_child_widget()); + version_info->set_layout_settings(ui::Margins { 0, 0, 10, 10 }); + auto* os_release = TRY(version_info->add_child_widget()); String os_release_text = TRY(String::format("OS release: %s"_sv, info.release)); - ui::Label os_release(os_release_text.view()); - version_info.add_widget(os_release); + os_release->set_text(os_release_text.view()); + auto* kernel_version = TRY(version_info->add_child_widget()); String kernel_version_text = TRY(String::format("Kernel version: %s"_sv, info.version)); - ui::Label kernel_version(kernel_version_text.view()); - version_info.add_widget(kernel_version); + kernel_version->set_text(kernel_version_text.view()); + + auto* license = TRY(version_info->add_child_widget()); + license->set_text("Licensed under the BSD-2-Clause license."); return app.run(); } diff --git a/apps/clock.cpp b/apps/clock.cpp index 9e307059..2afe4ec6 100644 --- a/apps/clock.cpp +++ b/apps/clock.cpp @@ -29,12 +29,12 @@ Result luna_main(int, char**) window->set_title("Clock"); window->set_background(ui::GRAY); - g_label = TRY(make("00:00:00")); + g_label = TRY(window->create_main_widget()); + + g_label->set_text("00:00:00"); g_label->set_font(ui::Font::default_bold_font()); g_label->set_color(ui::BLACK); - window->set_main_widget(*g_label); - update_time(); auto timer = TRY(os::Timer::create_repeating(1000, update_time)); diff --git a/apps/taskbar.cpp b/apps/taskbar.cpp index 18f273a3..64793d8d 100644 --- a/apps/taskbar.cpp +++ b/apps/taskbar.cpp @@ -30,24 +30,19 @@ void sighup_handler(int) os::Process::exec(args[0], { args, 1 }); } -Result create_widget_group_for_app(ui::HorizontalLayout& layout, StringView path, StringView icon) +Result create_widget_group_for_app(ui::HorizontalLayout* layout, StringView path, StringView icon) { - auto* button = TRY(make(ui::Rect { 0, 0, 50, 50 })); - layout.add_widget(*button); + auto* button = TRY(layout->add_child_widget()); - auto* container = TRY( - make(ui::Rect { 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center)); - button->set_widget(*container); + auto* container = TRY(button->create_child_widget()); button->set_action([=] { os::Launcher::LaunchDetachedRequest request; SET_IPC_STRING(request.command, path.chars()); launcher_client->send_async(request); }); - auto image = TRY(ui::ImageWidget::load(icon)); - container->set_widget(*image); - - image.leak(); + auto image = TRY(container->create_child_widget()); + image->load(icon); return {}; } @@ -165,8 +160,8 @@ Result luna_main(int, char**) window->set_background(TASKBAR_COLOR); window->set_special_attributes(ui::UNFOCUSEABLE); - ui::HorizontalLayout layout(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No); - window->set_main_widget(layout); + auto* layout = TRY(window->create_main_widget()); + layout->set_layout_settings(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No); load_app_files_from_path("/usr/share/applications/"); diff --git a/editor/EditorWidget.cpp b/editor/EditorWidget.cpp index 52d2392f..510c184a 100644 --- a/editor/EditorWidget.cpp +++ b/editor/EditorWidget.cpp @@ -15,7 +15,8 @@ #include #include -EditorWidget::EditorWidget(SharedPtr 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(); } diff --git a/editor/EditorWidget.h b/editor/EditorWidget.h index b824fbb6..b82cdd8c 100644 --- a/editor/EditorWidget.h +++ b/editor/EditorWidget.h @@ -16,7 +16,7 @@ class EditorWidget : public ui::TextInput { public: - EditorWidget(SharedPtr font); + EditorWidget(ui::Window* window, ui::Widget* parent); Result load_file(const os::Path& path); @@ -31,6 +31,11 @@ class EditorWidget : public ui::TextInput return m_path; } + void set_font(SharedPtr font) + { + m_font = font; + } + private: SharedPtr m_font; diff --git a/editor/main.cpp b/editor/main.cpp index e5e0acea..18f9f5a4 100644 --- a/editor/main.cpp +++ b/editor/main.cpp @@ -30,8 +30,7 @@ Result luna_main(int argc, char** argv) window->set_title("Text Editor"); app.set_main_window(window); - auto* editor = TRY(make(ui::Font::default_font())); - window->set_main_widget(*editor); + auto* editor = TRY(window->create_main_widget()); if (!path.is_empty()) TRY(editor->load_file(path)); TRY(window->add_keyboard_shortcut({ moon::K_CH26, ui::Mod_Ctrl }, true, [&](ui::Shortcut) { @@ -41,7 +40,5 @@ Result luna_main(int argc, char** argv) os::println("editor: buffer saved to %s successfully", editor->path().name().chars()); })); - window->draw(); - return app.run(); } diff --git a/libui/include/ui/Button.h b/libui/include/ui/Button.h index 38d5f512..01a0e337 100644 --- a/libui/include/ui/Button.h +++ b/libui/include/ui/Button.h @@ -9,16 +9,25 @@ #pragma once #include +#include #include +#include namespace ui { class Button : public Widget { public: - Button(Rect rect); + Button(ui::Window* window, ui::Widget* parent); + + template Result create_child_widget() + { + auto* widget = TRY(make(window(), this)); + m_child = widget; + window()->recalculate_layout(); + return widget; + } - void set_widget(Widget& widget); void set_action(os::Action&& action); Result handle_mouse_move(Point position) override; @@ -28,10 +37,20 @@ namespace ui Result handle_key_event(const ui::KeyEventRequest& request) override; Result 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: bool m_hovered { false }; bool m_clicked { false }; - Widget* m_child; + Widget* m_child { nullptr }; os::Action m_action; }; } diff --git a/libui/include/ui/Container.h b/libui/include/ui/Container.h index 36337d67..3dc05a40 100644 --- a/libui/include/ui/Container.h +++ b/libui/include/ui/Container.h @@ -8,17 +8,31 @@ */ #pragma once +#include #include #include +#include namespace ui { class Container : public Widget { 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 Result create_child_widget() + { + auto* widget = TRY(make(window(), this)); + m_widget = widget; + window()->recalculate_layout(); + return widget; + } Result handle_mouse_move(Point position) override; Result handle_mouse_leave() override; @@ -27,9 +41,19 @@ namespace ui Result handle_key_event(const ui::KeyEventRequest& request) override; Result 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: - Widget* m_widget; - VerticalAlignment m_valign; - HorizontalAlignment m_halign; + Widget* m_widget { nullptr }; + VerticalAlignment m_valign = VerticalAlignment::Center; + HorizontalAlignment m_halign = HorizontalAlignment::Center; }; } diff --git a/libui/include/ui/Image.h b/libui/include/ui/Image.h index cbcc5485..22939225 100644 --- a/libui/include/ui/Image.h +++ b/libui/include/ui/Image.h @@ -10,6 +10,7 @@ #pragma once #include #include +#include #include #include @@ -82,10 +83,20 @@ namespace ui class ImageWidget final : public Widget { public: - static Result> load(const os::Path& path); + ImageWidget(ui::Window* window, ui::Widget* parent); + + Result load(const os::Path& path); Result 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: SharedPtr m_image; }; diff --git a/libui/include/ui/InputField.h b/libui/include/ui/InputField.h index bd3bab56..6ee94fd6 100644 --- a/libui/include/ui/InputField.h +++ b/libui/include/ui/InputField.h @@ -17,7 +17,12 @@ namespace ui class InputField : public ui::TextInput { public: - InputField(SharedPtr font); + InputField(Window* window, Widget* parent); + + void set_font(SharedPtr font) + { + m_font = font; + } Result handle_key_event(const ui::KeyEventRequest& request) override; diff --git a/libui/include/ui/Label.h b/libui/include/ui/Label.h index 0de287ab..f30fb838 100644 --- a/libui/include/ui/Label.h +++ b/libui/include/ui/Label.h @@ -8,6 +8,7 @@ */ #pragma once +#include #include #include #include @@ -22,7 +23,7 @@ namespace ui class Label final : public Widget { public: - Label(StringView text); + Label(Window* window, Widget* parent); void set_alignment(VerticalAlignment valign, HorizontalAlignment halign) { @@ -47,6 +48,14 @@ namespace ui Result 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: StringView m_text; VerticalAlignment m_valign = VerticalAlignment::Center; diff --git a/libui/include/ui/Layout.h b/libui/include/ui/Layout.h index f405993e..7bd2c46b 100644 --- a/libui/include/ui/Layout.h +++ b/libui/include/ui/Layout.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include namespace ui @@ -36,8 +37,16 @@ namespace ui class HorizontalLayout final : public Widget { public: - HorizontalLayout(Margins margins = Margins { 0, 0, 0, 0 }, AdjustHeight adjust_height = AdjustHeight::Yes, - AdjustWidth adjust_width = AdjustWidth::Yes); + HorizontalLayout(ui::Window* window, ui::Widget* parent); + + 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 handle_mouse_move(Point position) override; Result handle_mouse_leave() override; @@ -47,21 +56,46 @@ namespace ui Result draw(Canvas& canvas) override; - Result add_widget(Widget& widget); + template Result add_child_widget() + { + auto* widget = TRY(make(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: Vector m_widgets; - Margins m_margins; - AdjustHeight m_adjust_height; - AdjustWidth m_adjust_width; + Margins m_margins { 0, 0, 0, 0 }; + AdjustHeight m_adjust_height = AdjustHeight::Yes; + AdjustWidth m_adjust_width = AdjustWidth::No; int m_used_width { 0 }; + + Result add_widget(Widget& widget); }; class VerticalLayout final : public Widget { public: - VerticalLayout(Margins margins = Margins { 0, 0, 0, 0 }, AdjustHeight adjust_height = AdjustHeight::Yes, - AdjustWidth adjust_width = AdjustWidth::Yes); + VerticalLayout(ui::Window* window, ui::Widget* parent); + + 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 handle_mouse_move(Point position) override; Result handle_mouse_leave() override; @@ -71,13 +105,30 @@ namespace ui Result draw(Canvas& canvas) override; - Result add_widget(Widget& widget); + template Result add_child_widget() + { + auto* widget = TRY(make(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: Vector m_widgets; - Margins m_margins; - AdjustHeight m_adjust_height; - AdjustWidth m_adjust_width; + Margins m_margins { 0, 0, 0, 0 }; + AdjustHeight m_adjust_height = AdjustHeight::No; + AdjustWidth m_adjust_width = AdjustWidth::Yes; int m_used_height { 0 }; + + Result add_widget(Widget& widget); }; } diff --git a/libui/include/ui/TextInput.h b/libui/include/ui/TextInput.h index 6d38b3a7..39f292c8 100644 --- a/libui/include/ui/TextInput.h +++ b/libui/include/ui/TextInput.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -17,12 +18,18 @@ namespace ui class TextInput : public Widget { public: - TextInput(); + TextInput(Window* window, Widget* parent); virtual Result handle_key_event(const ui::KeyEventRequest& request) = 0; virtual Result 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: Buffer m_data; diff --git a/libui/include/ui/Widget.h b/libui/include/ui/Widget.h index ea690f6e..bdba68d3 100644 --- a/libui/include/ui/Widget.h +++ b/libui/include/ui/Widget.h @@ -28,6 +28,10 @@ namespace ui class Widget { public: + Widget(Window* window, Widget* parent) : m_window(window), m_parent(parent) + { + } + virtual Result handle_mouse_move(Point position) { ignore(position); @@ -59,16 +63,14 @@ namespace ui virtual Result draw(Canvas& canvas); - void set_window(Window* window, Rect rect, Badge) + void set_rect(Rect rect, Badge) { - m_window = window; m_rect = rect; } - void set_parent(Widget* parent) + void resize(Rect rect) { - m_parent = parent; - m_window = parent->m_window; + m_preferred_rect = rect; } Widget* parent() @@ -86,9 +88,36 @@ namespace ui 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: - Widget* m_parent { nullptr }; Window* m_window; + Widget* m_parent { nullptr }; + + Option m_preferred_rect; + + Rect m_pseudo_rect; Rect m_rect { 0, 0, 50, 50 }; }; } diff --git a/libui/include/ui/Window.h b/libui/include/ui/Window.h index 83d4f00f..c269f65e 100644 --- a/libui/include/ui/Window.h +++ b/libui/include/ui/Window.h @@ -8,9 +8,12 @@ */ #pragma once +#include +#include #include #include #include +#include #include #include #include @@ -49,11 +52,13 @@ namespace ui m_background = color; } - void set_main_widget(Widget& widget) + template Result create_main_widget() { check(!m_main_widget); - widget.set_window(this, m_window_canvas.rect(), {}); - m_main_widget = &widget; + auto* widget = TRY(make(this, nullptr)); + m_main_widget = widget; + widget->set_rect(m_window_canvas.rect(), {}); + return widget; } Canvas& canvas() @@ -75,11 +80,25 @@ namespace ui Result add_keyboard_shortcut(ui::Shortcut shortcut, bool intercept, os::Function&& action); + void recalculate_layout() + { + check(m_main_widget); + m_main_widget->recalculate_pseudo_rects(); + m_main_widget->recalculate_true_rects(); + } + int id() const { 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(); private: diff --git a/libui/src/Button.cpp b/libui/src/Button.cpp index 14e5ceb1..a9dfa2bf 100644 --- a/libui/src/Button.cpp +++ b/libui/src/Button.cpp @@ -12,16 +12,26 @@ 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; - m_child = &widget; - widget.set_parent(this); + if (!m_child) return; + m_child->rect() = m_rect; + 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) diff --git a/libui/src/Container.cpp b/libui/src/Container.cpp index 9b4fb07f..a8b90f8b 100644 --- a/libui/src/Container.cpp +++ b/libui/src/Container.cpp @@ -11,17 +11,22 @@ namespace ui { - Container::Container(Rect rect, VerticalAlignment valign, HorizontalAlignment halign) - : m_valign(valign), m_halign(halign) + Container::Container(Window* window, Widget* parent) : ui::Widget(window, parent) { - m_rect = rect; } - void Container::set_widget(Widget& widget) + void Container::recalculate_true_rects() { - m_widget = &widget; - widget.rect() = ui::align(m_rect, widget.rect(), m_valign, m_halign); - widget.set_parent(this); + if (!m_widget) return; + m_widget->rect() = ui::align(m_rect, m_widget->pseudo_rect(), m_valign, m_halign); + m_widget->recalculate_true_rects(); + } + + void Container::recalculate_pseudo_rects() + { + if (m_widget) m_widget->recalculate_pseudo_rects(); + + m_pseudo_rect = preferred_rect(); } Result Container::handle_mouse_move(Point position) diff --git a/libui/src/Image.cpp b/libui/src/Image.cpp index f8b9d7f3..140f33ed 100644 --- a/libui/src/Image.cpp +++ b/libui/src/Image.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace ui { @@ -32,17 +33,28 @@ namespace ui return image; } - Result> ImageWidget::load(const os::Path& path) + ImageWidget::ImageWidget(Window* window, Widget* parent) : ui::Widget(window, parent) { - auto widget = TRY(make_owned()); - widget->m_image = TRY(Image::load(path)); - widget->m_rect = { 0, 0, widget->m_image->width(), widget->m_image->height() }; - return widget; + } + + Result ImageWidget::load(const os::Path& path) + { + m_image = TRY(Image::load(path)); + window()->recalculate_layout(); + return {}; } Result 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()); 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() }; + } } diff --git a/libui/src/InputField.cpp b/libui/src/InputField.cpp index a1e00eeb..602733b1 100644 --- a/libui/src/InputField.cpp +++ b/libui/src/InputField.cpp @@ -15,7 +15,8 @@ namespace ui { - InputField::InputField(SharedPtr font) : ui::TextInput(), m_font(font) + InputField::InputField(Window* window, Widget* parent) + : ui::TextInput(window, parent), m_font(ui::Font::default_font()) { } diff --git a/libui/src/Label.cpp b/libui/src/Label.cpp index 8dacb8fa..9b34489d 100644 --- a/libui/src/Label.cpp +++ b/libui/src/Label.cpp @@ -12,7 +12,7 @@ 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(); } @@ -33,4 +33,14 @@ namespace ui m_font->render(buf, m_color, subcanvas); 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 }; + } } diff --git a/libui/src/Layout.cpp b/libui/src/Layout.cpp index 5afefc99..b664847b 100644 --- a/libui/src/Layout.cpp +++ b/libui/src/Layout.cpp @@ -9,11 +9,11 @@ #include #include +#include namespace ui { - HorizontalLayout::HorizontalLayout(Margins margins, AdjustHeight adjust_height, AdjustWidth adjust_width) - : m_margins(margins), m_adjust_height(adjust_height), m_adjust_width(adjust_width) + HorizontalLayout::HorizontalLayout(Window* window, Widget* parent) : ui::Widget(window, parent) { } @@ -93,38 +93,85 @@ namespace ui { 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; - m_used_width += m_margins.left + widget.rect().width + m_margins.right; + w->recalculate_pseudo_rects(); + 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; 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 + 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); + + 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; + + 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; } - - widget.rect().pos.y = m_rect.pos.y + m_margins.top; - - if (m_adjust_height == AdjustHeight::Yes) + else { - 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) - : m_margins(margins), m_adjust_height(adjust_height), m_adjust_width(adjust_width) + VerticalLayout::VerticalLayout(Window* window, Widget* parent) : ui::Widget(window, parent) { } @@ -200,36 +247,84 @@ namespace ui return {}; } - Result 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; - m_used_height += m_margins.top + widget.rect().height + m_margins.bottom; + w->recalculate_pseudo_rects(); + 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; div_t result = div(m_rect.height, (int)m_widgets.size()); for (auto w : m_widgets) { 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); + + 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; + + 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; } - - widget.rect().pos.x = m_rect.pos.x + m_margins.left; - - if (m_adjust_width == AdjustWidth::Yes) + else { - 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 VerticalLayout::add_widget(Widget& widget) + { + TRY(m_widgets.try_append(&widget)); + + window()->recalculate_layout(); return {}; } diff --git a/libui/src/TextInput.cpp b/libui/src/TextInput.cpp index 0e47a84a..eb5493f4 100644 --- a/libui/src/TextInput.cpp +++ b/libui/src/TextInput.cpp @@ -12,7 +12,7 @@ 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(); } diff --git a/terminal/TerminalWidget.cpp b/terminal/TerminalWidget.cpp index 11db8b85..c6b48af8 100644 --- a/terminal/TerminalWidget.cpp +++ b/terminal/TerminalWidget.cpp @@ -35,6 +35,10 @@ static void sigchld_handler(int) ui::App::the().set_should_close(true); } +TerminalWidget::TerminalWidget(ui::Window* window, ui::Widget* parent) : ui::Widget(window, parent) +{ +} + Result TerminalWidget::init(char* const* args) { m_font = ui::Font::default_font(); diff --git a/terminal/TerminalWidget.h b/terminal/TerminalWidget.h index 375fb74e..6ad80e9f 100644 --- a/terminal/TerminalWidget.h +++ b/terminal/TerminalWidget.h @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -11,12 +12,20 @@ class TerminalWidget : public ui::Widget { public: + TerminalWidget(ui::Window* window, ui::Widget* parent); + Result init(char* const* args); Result handle_key_event(const ui::KeyEventRequest& request) override; Result 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: ui::Canvas m_terminal_canvas; Vector m_line_buffer; diff --git a/terminal/main.cpp b/terminal/main.cpp index 2006d9f5..208b4a59 100644 --- a/terminal/main.cpp +++ b/terminal/main.cpp @@ -12,13 +12,10 @@ Result luna_main(int, char**) app.set_main_window(window); window->set_title("Terminal"); - TerminalWidget terminal; - window->set_main_widget(terminal); + auto* terminal = TRY(window->create_main_widget()); char* args[] = { "/bin/sh", nullptr }; - TRY(terminal.init(args)); - - window->draw(); + TRY(terminal->init(args)); return app.run(); }