libui+wind: (Draggable) windows
All checks were successful
continuous-integration/drone/pr Build is passing

This commit is contained in:
apio 2023-08-03 17:38:49 +02:00
parent 7a2acce8fd
commit f3dbea044f
Signed by: apio
GPG Key ID: B8A7D06E42258954
12 changed files with 195 additions and 26 deletions

View File

@ -1,4 +1,6 @@
Name=login
Description=Start the display server.
Command=/usr/bin/wind --user=selene
StandardOutput=/dev/uart0
StandardError=/dev/uart0
Restart=true

View File

@ -17,14 +17,40 @@ namespace ui
int stride;
u8* ptr;
/**
* @brief Create a new Canvas object.
*
* @param ptr The memory to use for the canvas. It must be of at least width * height * 4 bytes of length.
* @param width The width of the canvas.
* @param height The height of the canvas.
* @return Canvas The new Canvas object.
*/
static Canvas create(u8* ptr, int width, int height);
Result<Canvas> subcanvas(Point begin, int width, int height, bool clamp);
/**
* @brief Return a new Canvas that represents a subsection of the current one.
*
* @param rect The dimensions of the new canvas. If these exceed the bounds of the current canvas, they will be
* clamped.
* @return Canvas The new Canvas object.
*/
Canvas subcanvas(Rect rect);
/**
* @brief Return the dimensions of the current canvas.
*
* @return Rect This canvas's dimensions, as a Rect object.
*/
Rect rect()
{
return Rect { .begin = { 0, 0 }, .width = width, .height = height };
return Rect { .pos = { 0, 0 }, .width = width, .height = height };
}
/**
* @brief Fill the entire canvas with one color.
*
* @param color The color to use.
*/
void fill(Color color);
};
};

View File

@ -4,7 +4,7 @@
namespace ui
{
/**
* @brief A 32-bit RGBA color.
* @brief A 32-bit ARGB color.
*/
struct Color
{
@ -13,16 +13,39 @@ namespace ui
u8 colors[4];
};
/**
* @brief Construct a new color from a 32-bit ARGB integer.
*
* @param raw The integer representing the color.
* @return constexpr Color The new color.
*/
static constexpr Color from_u32(u32 raw)
{
return Color { .raw = raw };
}
/**
* @brief Construct a new color from its separate RGBA values (from 0 to 255).
*
* @param red The red value.
* @param green The green value.
* @param blue The blue value.
* @param alpha The alpha value.
* @return constexpr Color The new color.
*/
static constexpr Color from_rgba(u8 red, u8 green, u8 blue, u8 alpha)
{
return Color { .colors = { blue, green, red, alpha } };
}
/**
* @brief Construct a new color from its separate RGB values (from 0 to 255).
*
* @param red The red value.
* @param green The green value.
* @param blue The blue value.
* @return constexpr Color The new color.
*/
static constexpr Color from_rgb(u8 red, u8 green, u8 blue)
{
return from_rgba(red, green, blue, 0xff);
@ -31,4 +54,10 @@ namespace ui
static constexpr Color WHITE = Color::from_rgb(0xff, 0xff, 0xff);
static constexpr Color BLACK = Color::from_rgb(0x00, 0x00, 0x00);
static constexpr Color BLUE = Color::from_rgb(0x00, 0x00, 0xff);
static constexpr Color GREEN = Color::from_rgb(0x00, 0xff, 0x00);
static constexpr Color RED = Color::from_rgb(0xff, 0x00, 0x00);
static constexpr Color CYAN = Color::from_rgb(0x00, 0xff, 0xff);
};

View File

@ -8,11 +8,32 @@ namespace ui
*/
struct Rect
{
Point begin;
Point pos;
int width;
int height;
/**
* @brief Check if a point is contained in this rectangle.
*
* @param point The point to check.
* @return true The point is contained inside the rectangle.
* @return false The point is not contained inside the rectangle.
*/
bool contains(Point point);
/**
* @brief Normalize a point to fit inside this rectangle.
*
* @param point The original point.
* @return Point The normalized point.
*/
Point normalize(Point point);
/**
* @brief Return a copy of this rectangle with no negative values (normalized to 0).
*
* @return Rect The new rectangle.
*/
Rect absolute();
};
}

View File

@ -8,22 +8,24 @@ namespace ui
return Canvas { .width = width, .height = height, .stride = width, .ptr = ptr };
}
Result<Canvas> Canvas::subcanvas(Point begin, int w, int h, bool clamp)
Canvas Canvas::subcanvas(Rect rect)
{
if (!clamp)
if (rect.pos.x < 0)
{
if (begin.x + w > width) return err(ERANGE);
if (begin.y + h > height) return err(ERANGE);
rect.pos.x = 0;
rect.width = rect.width + rect.pos.x;
}
else
if (rect.pos.y < 0)
{
if (begin.x + w > width) w = width - begin.x;
if (begin.y + h > height) h = height - begin.y;
rect.pos.y = 0;
rect.height = rect.height + rect.pos.y;
}
if (rect.pos.x + rect.width > width) rect.width = width - rect.pos.x;
if (rect.pos.y + rect.height > height) rect.height = height - rect.pos.y;
u8* p = ptr + begin.x * sizeof(Color) + (begin.y * sizeof(Color) * stride);
u8* p = ptr + rect.pos.x * sizeof(Color) + (rect.pos.y * sizeof(Color) * stride);
return Canvas { .width = w, .height = h, .stride = stride, .ptr = p };
return Canvas { .width = rect.width, .height = rect.height, .stride = stride, .ptr = p };
}
void Canvas::fill(Color color)

View File

@ -4,15 +4,21 @@ namespace ui
{
bool Rect::contains(Point point)
{
return point.x >= begin.x && point.y >= begin.y && point.x <= begin.x + width && point.y <= begin.y + height;
return (point.x >= pos.x) && (point.y >= pos.y) && (point.x <= (pos.x + width)) &&
(point.y <= (pos.y + height));
}
Point Rect::normalize(Point point)
{
if (point.x < begin.x) point.x = begin.x;
if (point.y < begin.y) point.y = begin.y;
if (point.x > begin.x + width) point.x = begin.x + width;
if (point.y > begin.y + height) point.y = begin.y + height;
if (point.x < pos.x) point.x = pos.x;
if (point.y < pos.y) point.y = pos.y;
if (point.x > pos.x + width) point.x = pos.x + width;
if (point.y > pos.y + height) point.y = pos.y + height;
return point;
}
Rect Rect::absolute()
{
return Rect { ui::Point { pos.x < 0 ? 0 : pos.x, pos.y < 0 ? 0 : pos.y }, width, height };
}
};

View File

@ -4,6 +4,8 @@ set(SOURCES
Screen.cpp
Mouse.h
Mouse.cpp
Window.h
Window.cpp
)
add_executable(wind ${SOURCES})

View File

@ -1,4 +1,5 @@
#include "Mouse.h"
#include <os/File.h>
Mouse::Mouse(ui::Canvas& screen)
{
@ -9,13 +10,44 @@ Mouse::Mouse(ui::Canvas& screen)
void Mouse::draw(ui::Canvas& screen)
{
auto canvas = screen.subcanvas(m_position, 10, 10, true).release_value();
auto canvas = screen.subcanvas(ui::Rect { m_position, 10, 10 });
canvas.fill(ui::WHITE);
}
void Mouse::move(const moon::MousePacket& packet)
void Mouse::update(const moon::MousePacket& packet)
{
m_position.x += packet.xdelta;
m_position.y -= packet.ydelta;
m_position = m_screen_rect.normalize(m_position);
if ((packet.buttons & moon::MouseButton::Left) && !m_dragging_window)
{
g_windows.for_each_reversed([this](Window* window) {
if (!this->m_dragging_window && window->surface.contains(this->m_position))
{
this->m_dragging_window = window;
this->m_initial_drag_position = ui::Point { this->m_position.x - window->surface.pos.x,
this->m_position.y - window->surface.pos.y };
os::println("Started drag: window at (%d,%d,%d,%d) with offset (%d,%d)", window->surface.pos.x,
window->surface.pos.y, window->surface.width, window->surface.height,
this->m_initial_drag_position.x, this->m_initial_drag_position.y);
}
});
}
else if (m_dragging_window && !(packet.buttons & moon::MouseButton::Left))
{
os::println("Stopped drag: window at (%d,%d,%d,%d) with offset (%d,%d)", m_dragging_window->surface.pos.x,
m_dragging_window->surface.pos.y, m_dragging_window->surface.width,
m_dragging_window->surface.height, this->m_initial_drag_position.x,
this->m_initial_drag_position.y);
m_dragging_window = nullptr;
}
if (m_dragging_window)
{
m_dragging_window->surface.pos =
ui::Point { m_position.x - m_initial_drag_position.x, m_position.y - m_initial_drag_position.y };
m_dragging_window->surface = m_dragging_window->surface.absolute();
m_dragging_window->focus();
}
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "Screen.h"
#include "Window.h"
#include <moon/Mouse.h>
#include <ui/Canvas.h>
@ -8,11 +9,14 @@ class Mouse
public:
Mouse(ui::Canvas& screen);
void move(const moon::MousePacket& packet);
void update(const moon::MousePacket& packet);
void draw(ui::Canvas& screen);
private:
ui::Point m_position;
ui::Rect m_screen_rect;
Window* m_dragging_window = nullptr;
ui::Point m_initial_drag_position;
};

20
wind/Window.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "Window.h"
LinkedList<Window> g_windows;
void Window::draw(ui::Canvas& screen)
{
screen.subcanvas(surface).fill(color);
}
void Window::focus()
{
// Bring the window to the front of the list.
g_windows.remove(this);
g_windows.append(this);
}
Window::Window(ui::Rect r, ui::Color c) : surface(r), color(c)
{
g_windows.append(this);
}

19
wind/Window.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <luna/LinkedList.h>
#include <ui/Canvas.h>
#include <ui/Color.h>
#include <ui/Rect.h>
struct Window : public LinkedListNode<Window>
{
ui::Rect surface;
ui::Color color;
Window(ui::Rect, ui::Color);
void focus();
void draw(ui::Canvas& screen);
};
extern LinkedList<Window> g_windows;

View File

@ -1,5 +1,6 @@
#include "Mouse.h"
#include "Screen.h"
#include "Window.h"
#include <errno.h>
#include <moon/Keyboard.h>
#include <os/ArgumentParser.h>
@ -71,10 +72,19 @@ Result<int> luna_main(int argc, char** argv)
ui::Color background = ui::BLACK;
TRY(make<Window>(ui::Rect { 200, 200, 600, 400 }, ui::GREEN));
TRY(make<Window>(ui::Rect { 100, 100, 300, 200 }, ui::RED));
TRY(make<Window>(ui::Rect { 600, 130, 350, 250 }, ui::CYAN));
Vector<SharedPtr<os::LocalServer::Client>> clients;
while (1)
{
screen.canvas().fill(background);
for (auto* window : g_windows) window->draw(screen.canvas());
mouse_pointer.draw(screen.canvas());
screen.sync();
struct pollfd fds[] = {
{ .fd = mouse->fd(), .events = POLLIN, .revents = 0 },
{ .fd = keyboard->fd(), .events = POLLIN, .revents = 0 },
@ -89,7 +99,7 @@ Result<int> luna_main(int argc, char** argv)
{
moon::MousePacket packet;
TRY(mouse->read_typed(packet));
mouse_pointer.move(packet);
mouse_pointer.update(packet);
}
if (fds[1].revents & POLLIN)
{
@ -103,9 +113,5 @@ Result<int> luna_main(int argc, char** argv)
os::println("wind: New client connected!");
TRY(clients.try_append(client));
}
screen.canvas().fill(background);
mouse_pointer.draw(screen.canvas());
screen.sync();
}
}