libui+wind: (Draggable) windows
This commit is contained in:
parent
335911c287
commit
8859fc3d6a
@ -1,4 +1,6 @@
|
|||||||
Name=login
|
Name=login
|
||||||
Description=Start the display server.
|
Description=Start the display server.
|
||||||
Command=/usr/bin/wind --user=selene
|
Command=/usr/bin/wind --user=selene
|
||||||
|
StandardOutput=/dev/uart0
|
||||||
|
StandardError=/dev/uart0
|
||||||
Restart=true
|
Restart=true
|
||||||
|
@ -17,14 +17,40 @@ namespace ui
|
|||||||
int stride;
|
int stride;
|
||||||
u8* ptr;
|
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);
|
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()
|
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);
|
void fill(Color color);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
namespace ui
|
namespace ui
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @brief A 32-bit RGBA color.
|
* @brief A 32-bit ARGB color.
|
||||||
*/
|
*/
|
||||||
struct Color
|
struct Color
|
||||||
{
|
{
|
||||||
@ -13,16 +13,39 @@ namespace ui
|
|||||||
u8 colors[4];
|
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)
|
static constexpr Color from_u32(u32 raw)
|
||||||
{
|
{
|
||||||
return Color { .raw = 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)
|
static constexpr Color from_rgba(u8 red, u8 green, u8 blue, u8 alpha)
|
||||||
{
|
{
|
||||||
return Color { .colors = { blue, green, red, 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)
|
static constexpr Color from_rgb(u8 red, u8 green, u8 blue)
|
||||||
{
|
{
|
||||||
return from_rgba(red, green, blue, 0xff);
|
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 WHITE = Color::from_rgb(0xff, 0xff, 0xff);
|
||||||
static constexpr Color BLACK = Color::from_rgb(0x00, 0x00, 0x00);
|
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);
|
||||||
};
|
};
|
||||||
|
@ -8,11 +8,32 @@ namespace ui
|
|||||||
*/
|
*/
|
||||||
struct Rect
|
struct Rect
|
||||||
{
|
{
|
||||||
Point begin;
|
Point pos;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
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);
|
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);
|
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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -8,22 +8,24 @@ namespace ui
|
|||||||
return Canvas { .width = width, .height = height, .stride = width, .ptr = ptr };
|
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);
|
rect.pos.x = 0;
|
||||||
if (begin.y + h > height) return err(ERANGE);
|
rect.width = rect.width + rect.pos.x;
|
||||||
}
|
}
|
||||||
else
|
if (rect.pos.y < 0)
|
||||||
{
|
{
|
||||||
if (begin.x + w > width) w = width - begin.x;
|
rect.pos.y = 0;
|
||||||
if (begin.y + h > height) h = height - begin.y;
|
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)
|
void Canvas::fill(Color color)
|
||||||
|
@ -4,15 +4,21 @@ namespace ui
|
|||||||
{
|
{
|
||||||
bool Rect::contains(Point point)
|
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)
|
Point Rect::normalize(Point point)
|
||||||
{
|
{
|
||||||
if (point.x < begin.x) point.x = begin.x;
|
if (point.x < pos.x) point.x = pos.x;
|
||||||
if (point.y < begin.y) point.y = begin.y;
|
if (point.y < pos.y) point.y = pos.y;
|
||||||
if (point.x > begin.x + width) point.x = begin.x + width;
|
if (point.x > pos.x + width) point.x = pos.x + width;
|
||||||
if (point.y > begin.y + height) point.y = begin.y + height;
|
if (point.y > pos.y + height) point.y = pos.y + height;
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rect Rect::absolute()
|
||||||
|
{
|
||||||
|
return Rect { ui::Point { pos.x < 0 ? 0 : pos.x, pos.y < 0 ? 0 : pos.y }, width, height };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,8 @@ set(SOURCES
|
|||||||
Screen.cpp
|
Screen.cpp
|
||||||
Mouse.h
|
Mouse.h
|
||||||
Mouse.cpp
|
Mouse.cpp
|
||||||
|
Window.h
|
||||||
|
Window.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(wind ${SOURCES})
|
add_executable(wind ${SOURCES})
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "Mouse.h"
|
#include "Mouse.h"
|
||||||
|
#include <os/File.h>
|
||||||
|
|
||||||
Mouse::Mouse(ui::Canvas& screen)
|
Mouse::Mouse(ui::Canvas& screen)
|
||||||
{
|
{
|
||||||
@ -9,13 +10,44 @@ Mouse::Mouse(ui::Canvas& screen)
|
|||||||
|
|
||||||
void Mouse::draw(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);
|
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.x += packet.xdelta;
|
||||||
m_position.y -= packet.ydelta;
|
m_position.y -= packet.ydelta;
|
||||||
m_position = m_screen_rect.normalize(m_position);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Screen.h"
|
#include "Screen.h"
|
||||||
|
#include "Window.h"
|
||||||
#include <moon/Mouse.h>
|
#include <moon/Mouse.h>
|
||||||
#include <ui/Canvas.h>
|
#include <ui/Canvas.h>
|
||||||
|
|
||||||
@ -8,11 +9,14 @@ class Mouse
|
|||||||
public:
|
public:
|
||||||
Mouse(ui::Canvas& screen);
|
Mouse(ui::Canvas& screen);
|
||||||
|
|
||||||
void move(const moon::MousePacket& packet);
|
void update(const moon::MousePacket& packet);
|
||||||
|
|
||||||
void draw(ui::Canvas& screen);
|
void draw(ui::Canvas& screen);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ui::Point m_position;
|
ui::Point m_position;
|
||||||
ui::Rect m_screen_rect;
|
ui::Rect m_screen_rect;
|
||||||
|
|
||||||
|
Window* m_dragging_window = nullptr;
|
||||||
|
ui::Point m_initial_drag_position;
|
||||||
};
|
};
|
||||||
|
20
wind/Window.cpp
Normal file
20
wind/Window.cpp
Normal 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
19
wind/Window.h
Normal 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;
|
@ -1,5 +1,6 @@
|
|||||||
#include "Mouse.h"
|
#include "Mouse.h"
|
||||||
#include "Screen.h"
|
#include "Screen.h"
|
||||||
|
#include "Window.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <moon/Keyboard.h>
|
#include <moon/Keyboard.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
@ -71,10 +72,19 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
ui::Color background = ui::BLACK;
|
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;
|
Vector<SharedPtr<os::LocalServer::Client>> clients;
|
||||||
|
|
||||||
while (1)
|
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[] = {
|
struct pollfd fds[] = {
|
||||||
{ .fd = mouse->fd(), .events = POLLIN, .revents = 0 },
|
{ .fd = mouse->fd(), .events = POLLIN, .revents = 0 },
|
||||||
{ .fd = keyboard->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;
|
moon::MousePacket packet;
|
||||||
TRY(mouse->read_typed(packet));
|
TRY(mouse->read_typed(packet));
|
||||||
mouse_pointer.move(packet);
|
mouse_pointer.update(packet);
|
||||||
}
|
}
|
||||||
if (fds[1].revents & POLLIN)
|
if (fds[1].revents & POLLIN)
|
||||||
{
|
{
|
||||||
@ -103,9 +113,5 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
os::println("wind: New client connected!");
|
os::println("wind: New client connected!");
|
||||||
TRY(clients.try_append(client));
|
TRY(clients.try_append(client));
|
||||||
}
|
}
|
||||||
|
|
||||||
screen.canvas().fill(background);
|
|
||||||
mouse_pointer.draw(screen.canvas());
|
|
||||||
screen.sync();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user