Compare commits

...

5 Commits

Author SHA1 Message Date
fd62ea588d
libui: Reduce redraw calls by doing them only when events are actually handled
All checks were successful
continuous-integration/drone/pr Build is passing
2023-09-11 19:38:29 +02:00
fc656acc2c
apps: Add about 2023-09-11 19:15:26 +02:00
4be9753ad3
libui: Add a basic Label component 2023-09-11 19:15:18 +02:00
93427425cb
libui: Clarify that Font is only used for low-level glyph rendering 2023-09-11 19:14:17 +02:00
d892e431b3
libui: Zero-initialize counter variables in Layout 2023-09-11 07:31:31 +02:00
13 changed files with 204 additions and 38 deletions

View File

@ -42,5 +42,7 @@ luna_app(touch.cpp touch)
luna_app(free.cpp free) luna_app(free.cpp free)
luna_app(gclient.cpp gclient) luna_app(gclient.cpp gclient)
target_link_libraries(gclient PUBLIC ui) target_link_libraries(gclient PUBLIC ui)
luna_app(about.cpp about)
target_link_libraries(about PUBLIC ui)
luna_app(taskbar.cpp taskbar) luna_app(taskbar.cpp taskbar)
target_link_libraries(taskbar PUBLIC ui) target_link_libraries(taskbar PUBLIC ui)

46
apps/about.cpp Normal file
View File

@ -0,0 +1,46 @@
#include <luna/String.h>
#include <sys/utsname.h>
#include <ui/App.h>
#include <ui/Button.h>
#include <ui/Label.h>
#include <ui/Layout.h>
Result<int> luna_main(int argc, char** argv)
{
ui::App app;
TRY(app.init(argc, argv));
auto* window = TRY(ui::Window::create(ui::Rect { 300, 300, 400, 300 }));
app.set_main_window(window);
window->set_title("About");
window->set_background(ui::CYAN);
utsname info;
uname(&info);
ui::VerticalLayout main_layout;
window->set_main_widget(main_layout);
ui::Label title("About Luna", ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center,
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);
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);
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);
window->draw();
return app.run();
}

View File

@ -10,14 +10,16 @@ struct ColorWidget : public ui::Widget
Result<ui::EventResult> handle_mouse_move(ui::Point) override Result<ui::EventResult> handle_mouse_move(ui::Point) override
{ {
auto old_color = m_color;
m_color = m_second_color; m_color = m_second_color;
return ui::EventResult::DidHandle; return old_color.raw == m_second_color.raw ? ui::EventResult::DidNotHandle : ui::EventResult::DidHandle;
} }
Result<ui::EventResult> handle_mouse_leave() override Result<ui::EventResult> handle_mouse_leave() override
{ {
auto old_color = m_color;
m_color = m_first_color; m_color = m_first_color;
return ui::EventResult::DidHandle; return old_color.raw == m_first_color.raw ? ui::EventResult::DidNotHandle : ui::EventResult::DidHandle;
} }
Result<void> draw(ui::Canvas& canvas) override Result<void> draw(ui::Canvas& canvas) override
@ -40,7 +42,7 @@ Result<int> luna_main(int argc, char** argv)
auto* window = TRY(ui::Window::create(ui::Rect { 200, 200, 400, 300 })); auto* window = TRY(ui::Window::create(ui::Rect { 200, 200, 400, 300 }));
app.set_main_window(window); app.set_main_window(window);
window->set_title("Main Window"); window->set_title("Test Window");
window->set_background(ui::CYAN); window->set_background(ui::CYAN);
ui::HorizontalLayout layout; ui::HorizontalLayout layout;

View File

@ -1,3 +1,4 @@
#include <os/File.h>
#include <os/Process.h> #include <os/Process.h>
#include <signal.h> #include <signal.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -30,18 +31,31 @@ Result<int> luna_main(int argc, char** argv)
ui::HorizontalLayout layout(ui::AdjustHeight::Yes, ui::AdjustWidth::No); ui::HorizontalLayout layout(ui::AdjustHeight::Yes, ui::AdjustWidth::No);
window->set_main_widget(layout); window->set_main_widget(layout);
ui::Button button({ 0, 0, 50, 50 }); ui::Button start_button({ 0, 0, 50, 50 });
layout.add_widget(button); layout.add_widget(start_button);
ui::Container container({ 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center); ui::Container start_container({ 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center);
button.set_widget(container); start_button.set_widget(start_container);
button.set_action([] { start_button.set_action([] {
StringView args[] = { "/usr/bin/gclient" }; StringView args[] = { "/usr/bin/gclient" };
os::Process::spawn("/usr/bin/gclient", Slice<StringView> { args, 1 }, false); os::Process::spawn("/usr/bin/gclient", Slice<StringView> { args, 1 }, false);
}); });
auto image = TRY(ui::ImageWidget::load("/usr/share/icons/32x32/start-icon.tga")); auto start_image = TRY(ui::ImageWidget::load("/usr/share/icons/32x32/start-icon.tga"));
container.set_widget(*image); start_container.set_widget(*start_image);
ui::Button about_button({ 0, 0, 50, 50 });
layout.add_widget(about_button);
ui::Container about_container({ 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center);
about_button.set_widget(about_container);
about_button.set_action([] {
StringView args[] = { "/usr/bin/about" };
os::Process::spawn("/usr/bin/about", Slice<StringView> { args, 1 }, false);
});
auto about_image = TRY(ui::ImageWidget::load("/usr/share/icons/32x32/app-about.tga"));
about_container.set_widget(*about_image);
window->draw(); window->draw();

View File

@ -16,6 +16,7 @@ set(SOURCES
src/Alignment.cpp src/Alignment.cpp
src/Container.cpp src/Container.cpp
src/Button.cpp src/Button.cpp
src/Label.cpp
) )
add_library(ui ${SOURCES}) add_library(ui ${SOURCES})

View File

@ -18,7 +18,10 @@
namespace ui namespace ui
{ {
/** /**
* @brief A class holding PSF font data, used for direct rendering of glyphs into a canvas. * @brief A class holding PSF font data, used for low-level direct rendering of glyphs into a canvas.
*
* This class does not handle special characters such as tabs or newlines. For those, you should be using a more
* high-level component such as ui::Label instead.
*/ */
class Font : public Shareable class Font : public Shareable
{ {

41
libui/include/ui/Label.h Normal file
View File

@ -0,0 +1,41 @@
/**
* @file Label.h
* @author apio (cloudapio.eu)
* @brief A simple one-line text widget.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <ui/Alignment.h>
#include <ui/Font.h>
#include <ui/Widget.h>
namespace ui
{
/**
* @brief Displays one line of text.
*
* This component does not handle newlines.
*/
class Label final : public Widget
{
public:
Label(StringView text, VerticalAlignment valign = VerticalAlignment::Center,
HorizontalAlignment halign = HorizontalAlignment::Center, SharedPtr<Font> font = Font::default_font());
void set_text(StringView text)
{
m_text = text;
}
Result<void> draw(Canvas& canvas) override;
private:
StringView m_text;
VerticalAlignment m_valign;
HorizontalAlignment m_halign;
SharedPtr<Font> m_font;
};
}

View File

@ -43,7 +43,7 @@ namespace ui
Vector<Widget*> m_widgets; Vector<Widget*> m_widgets;
AdjustHeight m_adjust_height; AdjustHeight m_adjust_height;
AdjustWidth m_adjust_width; AdjustWidth m_adjust_width;
int m_used_width; int m_used_width { 0 };
}; };
class VerticalLayout final : public Widget class VerticalLayout final : public Widget
@ -64,6 +64,6 @@ namespace ui
Vector<Widget*> m_widgets; Vector<Widget*> m_widgets;
AdjustHeight m_adjust_height; AdjustHeight m_adjust_height;
AdjustWidth m_adjust_width; AdjustWidth m_adjust_width;
int m_used_height; int m_used_height { 0 };
}; };
} }

View File

@ -46,9 +46,9 @@ namespace ui
void close(); void close();
Result<void> draw(); Result<void> draw();
Result<void> handle_mouse_leave(); Result<ui::EventResult> handle_mouse_leave();
Result<void> handle_mouse_move(ui::Point position); Result<ui::EventResult> handle_mouse_move(ui::Point position);
Result<void> handle_mouse_buttons(ui::Point position, int buttons); Result<ui::EventResult> handle_mouse_buttons(ui::Point position, int buttons);
int id() const int id() const
{ {

View File

@ -117,17 +117,19 @@ namespace ui
MouseEventRequest request; MouseEventRequest request;
READ_MESSAGE(request); READ_MESSAGE(request);
auto* window = find_window(request.window); auto* window = find_window(request.window);
window->handle_mouse_move(request.position); auto move_result = window->handle_mouse_move(request.position).value_or(ui::EventResult::DidNotHandle);
window->handle_mouse_buttons(request.position, request.buttons); auto button_result =
window->draw(); window->handle_mouse_buttons(request.position, request.buttons).value_or(ui::EventResult::DidNotHandle);
if (move_result == ui::EventResult::DidHandle || button_result == ui::EventResult::DidHandle)
window->draw();
return {}; return {};
} }
case MOUSE_LEAVE_REQUEST_ID: { case MOUSE_LEAVE_REQUEST_ID: {
MouseLeaveRequest request; MouseLeaveRequest request;
READ_MESSAGE(request); READ_MESSAGE(request);
auto* window = find_window(request.window); auto* window = find_window(request.window);
window->handle_mouse_leave(); if (window->handle_mouse_leave().value_or(ui::EventResult::DidNotHandle) == ui::EventResult::DidHandle)
window->draw(); window->draw();
return {}; return {};
} }
default: fail("Unexpected IPC request from server!"); default: fail("Unexpected IPC request from server!");

36
libui/src/Label.cpp Normal file
View File

@ -0,0 +1,36 @@
/**
* @file Label.cpp
* @author apio (cloudapio.eu)
* @brief A simple one-line text widget.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <luna/Utf8.h>
#include <ui/Label.h>
namespace ui
{
Label::Label(StringView text, VerticalAlignment valign, HorizontalAlignment halign, SharedPtr<Font> font)
: m_text(text), m_valign(valign), m_halign(halign), m_font(font)
{
}
Result<void> Label::draw(Canvas& canvas)
{
ui::Rect contained;
contained.pos = { 0, 0 };
contained.width = static_cast<int>(m_text.length() * m_font->width());
contained.height = m_font->height();
auto subcanvas =
canvas.subcanvas(ui::align({ 0, 0, m_rect.width, m_rect.height }, contained, m_valign, m_halign));
Utf8StringDecoder decoder(m_text.chars());
wchar_t buf[4096];
TRY(decoder.decode(buf, sizeof(buf)));
m_font->render(buf, ui::BLACK, subcanvas);
return {};
}
}

View File

@ -33,9 +33,15 @@ namespace ui
Result<EventResult> HorizontalLayout::handle_mouse_leave() Result<EventResult> HorizontalLayout::handle_mouse_leave()
{ {
for (auto widget : m_widgets) TRY(widget->handle_mouse_leave()); EventResult result = ui::EventResult::DidNotHandle;
return ui::EventResult::DidNotHandle; for (auto widget : m_widgets)
{
auto rc = TRY(widget->handle_mouse_leave());
if (rc == ui::EventResult::DidHandle) result = rc;
}
return result;
} }
Result<EventResult> HorizontalLayout::handle_mouse_up(Point position, int buttons) Result<EventResult> HorizontalLayout::handle_mouse_up(Point position, int buttons)
@ -122,9 +128,15 @@ namespace ui
Result<EventResult> VerticalLayout::handle_mouse_leave() Result<EventResult> VerticalLayout::handle_mouse_leave()
{ {
for (auto widget : m_widgets) TRY(widget->handle_mouse_leave()); EventResult result = ui::EventResult::DidNotHandle;
return ui::EventResult::DidNotHandle; for (auto widget : m_widgets)
{
auto rc = TRY(widget->handle_mouse_leave());
if (rc == ui::EventResult::DidHandle) result = rc;
}
return result;
} }
Result<EventResult> VerticalLayout::handle_mouse_up(Point position, int buttons) Result<EventResult> VerticalLayout::handle_mouse_up(Point position, int buttons)

View File

@ -80,31 +80,38 @@ namespace ui
return {}; return {};
} }
Result<void> Window::handle_mouse_leave() Result<ui::EventResult> Window::handle_mouse_leave()
{ {
if (!m_main_widget) return {}; if (!m_main_widget) return ui::EventResult::DidNotHandle;
TRY(m_main_widget->handle_mouse_leave()); return m_main_widget->handle_mouse_leave();
return {};
} }
Result<void> Window::handle_mouse_move(ui::Point position) Result<ui::EventResult> Window::handle_mouse_move(ui::Point position)
{ {
if (!m_main_widget) return {}; if (!m_main_widget) return ui::EventResult::DidNotHandle;
TRY(m_main_widget->handle_mouse_move(position)); return m_main_widget->handle_mouse_move(position);
return {};
} }
Result<void> Window::handle_mouse_buttons(ui::Point position, int buttons) Result<ui::EventResult> Window::handle_mouse_buttons(ui::Point position, int buttons)
{ {
if (!m_main_widget) return {}; if (!m_main_widget) return ui::EventResult::DidNotHandle;
if (buttons) TRY(m_main_widget->handle_mouse_down(position, buttons)); auto result = ui::EventResult::DidNotHandle;
if (buttons)
{
auto rc = TRY(m_main_widget->handle_mouse_down(position, buttons));
if (rc == ui::EventResult::DidHandle) result = rc;
}
if (m_old_mouse_buttons.has_value()) if (m_old_mouse_buttons.has_value())
{ {
int old_buttons = m_old_mouse_buttons.value(); int old_buttons = m_old_mouse_buttons.value();
int diff = old_buttons & ~buttons; int diff = old_buttons & ~buttons;
if (diff) TRY(m_main_widget->handle_mouse_up(position, diff)); if (diff)
{
auto rc = TRY(m_main_widget->handle_mouse_up(position, diff));
if (rc == ui::EventResult::DidHandle) result = rc;
}
} }
m_old_mouse_buttons = buttons; m_old_mouse_buttons = buttons;
return {}; return result;
} }
} }