Squash commits for public release

This commit is contained in:
2025-02-12 09:54:05 -05:00
commit 7118adc514
1108 changed files with 80873 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
#pragma once
#include <libfoundation/Event.h>
#include <libfoundation/EventLoop.h>
#include <libfoundation/EventReceiver.h>
#include <libui/AppDelegate.h>
#include <libui/Connection.h>
#include <memory>
#include <sys/types.h>
namespace UI {
class Window;
enum class AppState {
Active,
Background,
};
class App : public LFoundation::EventReceiver {
public:
inline static App& the()
{
extern App* s_UI_App_the;
return *s_UI_App_the;
}
App();
inline int run() { return m_event_loop.run(); }
inline LFoundation::EventLoop& event_loop() { return m_event_loop; }
inline const LFoundation::EventLoop& event_loop() const { return m_event_loop; }
void set_window(Window* window) { m_window = window; }
inline Window& window() { return *m_window; }
inline const Window& window() const { return *m_window; }
void set_delegate(AppDelegate* delegate) { m_delegate = delegate; }
inline AppDelegate* delegate() { return m_delegate; }
inline Connection& connection() { return m_server_connection; }
inline const Connection& connection() const { return m_server_connection; }
void receive_event(std::unique_ptr<LFoundation::Event> event) override;
void set_state(AppState state) { m_state = state; }
AppState state() const { return m_state; }
private:
LFoundation::EventLoop m_event_loop;
Connection m_server_connection;
AppDelegate* m_delegate { nullptr };
Window* m_window { nullptr };
AppState m_state;
};
} // namespace UI

View File

@@ -0,0 +1,43 @@
#pragma once
#include <libfoundation/Event.h>
#include <libfoundation/EventLoop.h>
#include <libfoundation/EventReceiver.h>
#include <libg/Size.h>
#include <libui/Connection.h>
#include <libui/Window.h>
#include <memory>
#include <sys/types.h>
#define SET_APP_DELEGATE(name) \
name* MainAppDelegatePtr; \
extern "C" bool __init_app_delegate(UI::AppDelegate** res) \
{ \
MainAppDelegatePtr = new name(); \
*res = MainAppDelegatePtr; \
return MainAppDelegatePtr->application(); \
}
namespace UI {
class AppDelegate {
public:
AppDelegate() = default;
virtual ~AppDelegate() = default;
LG::Size window_size() const
{
#ifdef TARGET_DESKTOP
return preferred_desktop_window_size();
#elif TARGET_MOBILE
return LG::Size(320, 548);
#endif
}
virtual LG::Size preferred_desktop_window_size() const { return LG::Size(400, 300); }
virtual const char* icon_path() const { return "/res/icons/apps/missing.icon"; }
virtual bool application() { return false; }
virtual void application_will_terminate() { }
private:
};
} // namespace UI

View File

@@ -0,0 +1,64 @@
#pragma once
#include <libg/Font.h>
#include <libui/Constants/Text.h>
#include <libui/Control.h>
#include <libui/EdgeInsets.h>
#include <string>
namespace UI {
class Button : public Control {
UI_OBJECT();
public:
enum Type {
System,
Custom,
};
~Button() = default;
const std::string& title() const { return m_title; }
void set_title(const std::string& title) { m_title = title, recalc_bounds(), set_needs_display(); }
void set_title(std::string&& title) { m_title = std::move(title), recalc_bounds(), set_needs_display(); }
void set_title_color(const LG::Color& color) { m_title_color = color; }
const LG::Color& title_color() const { return m_title_color; }
void set_content_edge_insets(const EdgeInsets& ei) { m_content_edge_insets = ei, recalc_bounds(); }
const EdgeInsets& content_edge_insets() const { return m_content_edge_insets; }
void set_font(const LG::Font& font) { m_font = font, recalc_bounds(); }
inline const LG::Font& font() const { return m_font; }
void set_alignment(Text::Alignment alignment) { m_alignment = alignment; }
Text::Alignment alignment() const { return m_alignment; }
virtual void display(const LG::Rect& rect) override;
virtual void mouse_entered(const LG::Point<int>& location) override;
virtual void mouse_exited() override;
virtual void mouse_down(const LG::Point<int>& location) override;
virtual void mouse_up() override;
void set_type(Type type) { m_button_type = type; }
protected:
Button(View* superview, const LG::Rect& frame);
private:
void recalc_bounds();
size_t text_height() const;
size_t text_width();
Type m_button_type { Type::System };
std::string m_title {};
LG::Color m_title_color { LG::Color::White };
LG::Font m_font { LG::Font::system_font() };
Text::Alignment m_alignment { Text::Alignment::Left };
EdgeInsets m_content_edge_insets { 12, 12, 12, 12 };
};
} // namespace UI

View File

@@ -0,0 +1,36 @@
#pragma once
#include <libapi/window_server/Connections/WSConnection.h>
#include <libfoundation/EventLoop.h>
namespace UI {
class App;
class ClientDecoder : public BaseWindowClientDecoder {
public:
ClientDecoder();
~ClientDecoder() = default;
using BaseWindowClientDecoder::handle;
virtual std::unique_ptr<Message> handle(MouseMoveMessage& msg) override;
virtual std::unique_ptr<Message> handle(MouseActionMessage& msg) override;
virtual std::unique_ptr<Message> handle(MouseLeaveMessage& msg) override;
virtual std::unique_ptr<Message> handle(MouseWheelMessage& msg) override;
virtual std::unique_ptr<Message> handle(KeyboardMessage& msg) override;
virtual std::unique_ptr<Message> handle(DisplayMessage& msg) override;
virtual std::unique_ptr<Message> handle(WindowCloseRequestMessage& msg) override;
virtual std::unique_ptr<Message> handle(ResizeMessage& msg) override;
virtual std::unique_ptr<Message> handle(MenuBarActionMessage& msg) override;
virtual std::unique_ptr<Message> handle(PopupActionMessage& msg) override;
// Notifiers
virtual std::unique_ptr<Message> handle(NotifyWindowCreateMessage& msg) override;
virtual std::unique_ptr<Message> handle(NotifyWindowStatusChangedMessage& msg) override;
virtual std::unique_ptr<Message> handle(NotifyWindowIconChangedMessage& msg) override;
virtual std::unique_ptr<Message> handle(NotifyWindowTitleChangedMessage& msg) override;
private:
LFoundation::EventLoop& m_event_loop;
};
} // namespace UI

View File

@@ -0,0 +1,74 @@
#pragma once
#include <functional>
#include <libg/Size.h>
#include <libui/Constants/Layout.h>
#include <libui/EdgeInsets.h>
#include <libui/ScrollView.h>
#include <list>
#include <string>
#include <utility>
namespace UI {
// The CollectionView in UIKit is devided into sections where each section contains a number
// of elements. This behavior is not implemented, instead of CollectionViewDataSource protocol
// just a callback CollectionViewRowStreamer is used. It returns the whole line to be rendered.
typedef std::function<View*(int)> CollectionViewRowStreamer;
class CollectionView : public ScrollView {
UI_OBJECT();
public:
~CollectionView() = default;
template <class T>
void set_data_source(T data_source) { m_data_source = data_source, prefetch_forward(); };
void reload_data() { prefetch_forward(); }
void invalidate_row(int id);
virtual void display(const LG::Rect& rect) override;
virtual void mouse_entered(const LG::Point<int>& location) override;
virtual void mouse_exited() override;
virtual WheelEventResponse mouse_wheel_event(int wheel_data) override
{
ScrollView::mouse_wheel_event(wheel_data);
if (wheel_data > 0) {
after_scroll_forward();
prefetch_forward();
} else {
after_scroll_backward();
}
return WheelEventResponse::Handled;
}
protected:
CollectionView(View* superview, const LG::Rect&);
CollectionView(View* superview, Window* window, const LG::Rect& frame);
private:
enum PrefetchStatus {
Success,
EndOfStream,
};
PrefetchStatus prefetch_row_forward(int id);
void prefetch_forward();
void after_scroll_forward();
void after_scroll_backward();
// Cache of views which precede the first on-screen view.
std::list<View*> m_preceding_views {};
// Cache of views which follow the last on-screen view.
std::list<View*> m_following_views {};
std::list<View*> m_views_on_screen {};
size_t m_first_onscreen_row_index { 0 };
size_t m_first_offscreen_row_index { 0 };
LG::Point<int> m_next_frame_origin { 0, 16 };
CollectionViewRowStreamer m_data_source;
};
} // namespace UI

View File

@@ -0,0 +1,75 @@
#pragma once
#include <functional>
#include <libipc/VectorEncoder.h>
#include <string>
#include <utility>
namespace UI {
class Menu;
class MenuBar;
class MenuItem {
friend class Menu;
friend class MenuBar;
public:
MenuItem(const std::string& title, std::function<void()> action)
: m_title(title)
, m_action(action)
{
}
MenuItem(std::string&& title, std::function<void()> action)
: m_title(std::move(title))
, m_action(action)
{
}
inline const std::string& title() const { return m_title; }
inline void invoke() { m_action(); }
private:
inline void set_id(uint32_t id) { m_id = id; }
std::string m_title;
std::function<void()> m_action;
uint32_t m_id;
};
class Menu {
friend class MenuBar;
public:
Menu() = default;
Menu(const std::string& title)
: m_title(title)
{
}
Menu(std::string&& title)
: m_title(std::move(title))
{
}
inline void add_item(const MenuItem& item)
{
int id = m_menu_items.size();
m_menu_items.push_back(item);
m_menu_items.back().set_id(id);
}
inline const std::string& title() const { return m_title; }
inline uint32_t menu_id() const { return m_menu_id; }
std::vector<MenuItem>& items() { return m_menu_items; }
const std::vector<MenuItem>& items() const { return m_menu_items; }
private:
inline void set_menu_id(uint32_t id) { m_menu_id = id; }
std::string m_title {};
uint32_t m_menu_id;
std::vector<MenuItem> m_menu_items;
};
}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <libipc/ClientConnection.h>
#include <libui/ClientDecoder.h>
#include <sys/types.h>
namespace UI {
class Window;
class Connection {
public:
static Connection& the();
explicit Connection(const LIPC::DoubleSidedConnection&);
void greeting();
int new_window(const Window& window);
void set_buffer(const Window& window);
template <class T>
inline std::unique_ptr<T> send_sync_message(const Message& msg) { return std::unique_ptr<T>(m_connection_with_server.send_sync(msg)); }
inline bool send_async_message(const Message& msg) const { return m_connection_with_server.send_message(msg); }
inline void listen() { m_connection_with_server.pump_messages(); }
// We use connection id as an unique key.
inline int key() const { return m_connection_id; }
private:
void setup_listners();
int m_connection_id;
LIPC::DoubleSidedConnection m_connection;
ClientConnection<BaseWindowServerDecoder, ClientDecoder> m_connection_with_server;
BaseWindowServerDecoder m_server_decoder;
ClientDecoder m_client_decoder;
};
} // namespace UI

View File

@@ -0,0 +1,10 @@
#pragma once
namespace UI::LayoutConstraints {
enum class Axis {
Horizontal,
Vertical
};
} // namespace UI::LayoutConstraints

View File

@@ -0,0 +1,11 @@
#pragma once
namespace UI::Text {
enum class Alignment {
Left,
Center,
Right,
};
} // namespace UI::Text

View File

@@ -0,0 +1,132 @@
#pragma once
namespace UI {
class View;
class Constraint {
public:
enum class Attribute {
NotAnAttr,
Left,
Right,
Top,
Bottom,
CenterX,
CenterY,
Width,
Height,
};
enum class Relation {
Equal,
// LessThanOrEqual,
// GreaterThanOrEqual,
};
Constraint(View& item, Constraint::Attribute attr, Constraint::Relation related_by, View& rel_item, Constraint::Attribute to_attr, int multiplier, int constant)
: m_item(&item)
, m_attr(attr)
, m_related_by(related_by)
, m_rel_item(&rel_item)
, m_to_attr(to_attr)
, m_multiplier(multiplier)
, m_constant(constant)
{
}
Constraint(View& item, Constraint::Attribute attr, Constraint::Relation related_by, int constant)
: m_item(&item)
, m_attr(attr)
, m_related_by(related_by)
, m_rel_item(nullptr)
, m_to_attr(Constraint::Attribute::NotAnAttr)
, m_multiplier(1)
, m_constant(constant)
{
}
~Constraint() = default;
UI::View* item() const { return m_item; }
Constraint::Attribute attribute() const { return m_attr; }
Constraint::Relation relation() const { return m_related_by; }
UI::View* rel_item() const { return m_rel_item; }
Constraint::Attribute rel_attribute() const { return m_to_attr; }
int multiplier() const { return m_multiplier; }
int constant() const { return m_constant; }
template <typename T>
static inline T get_attribute(const LG::Rect& rect, UI::Constraint::Attribute attr)
{
switch (attr) {
case UI::Constraint::Attribute::Top:
return (T)rect.min_y();
case UI::Constraint::Attribute::Bottom:
return (T)rect.max_y();
case UI::Constraint::Attribute::Left:
return (T)rect.min_x();
case UI::Constraint::Attribute::Right:
return (T)rect.max_x();
case UI::Constraint::Attribute::CenterX:
return (T)rect.mid_x();
case UI::Constraint::Attribute::CenterY:
return (T)rect.mid_y();
case UI::Constraint::Attribute::Width:
return (T)rect.width();
case UI::Constraint::Attribute::Height:
return (T)rect.height();
default:
return (T)0;
}
}
template <UI::Constraint::Attribute attr, typename T>
static constexpr inline void set_attribute(LG::Rect& rect, T m_value)
{
if constexpr (attr == UI::Constraint::Attribute::Top) {
rect.set_y(m_value);
return;
} else if constexpr (attr == UI::Constraint::Attribute::Bottom) {
rect.set_y(m_value - rect.height());
return;
} else if constexpr (attr == UI::Constraint::Attribute::Left) {
rect.set_x(m_value);
return;
} else if constexpr (attr == UI::Constraint::Attribute::Right) {
rect.set_x(m_value - rect.width());
return;
} else if constexpr (attr == UI::Constraint::Attribute::CenterX) {
rect.set_x(m_value - (rect.width() / 2));
return;
} else if constexpr (attr == UI::Constraint::Attribute::CenterY) {
rect.set_y(m_value - (rect.height() / 2));
return;
} else if constexpr (attr == UI::Constraint::Attribute::Width) {
rect.set_width(m_value);
return;
} else if constexpr (attr == UI::Constraint::Attribute::Height) {
rect.set_height(m_value);
return;
}
}
private:
UI::View* m_item;
Constraint::Attribute m_attr;
Constraint::Relation m_related_by;
UI::View* m_rel_item;
Constraint::Attribute m_to_attr;
int m_multiplier { 0 };
int m_constant { 0 };
};
} // namespace UI

View File

@@ -0,0 +1,52 @@
#pragma once
#include <libg/Context.h>
#include <libui/ContextManager.h>
#include <libui/View.h>
#include <libui/Window.h>
namespace UI {
class Context : public LG::Context {
public:
enum RelativeToCurrentContext {
Yes
};
explicit Context(LG::PixelBitmap& bitmap)
: LG::Context(bitmap)
{
}
explicit Context(View& view)
: Context(view.window()->bitmap())
{
auto frame = view.frame_in_window();
add_clip(frame);
set_draw_offset(frame.origin());
}
Context(View& view, const LG::Rect& frame)
: Context(view.window()->bitmap())
{
add_clip(frame);
set_draw_offset(frame.origin());
}
Context(View& view, RelativeToCurrentContext)
: Context(view.window()->bitmap())
{
auto context_frame = view.frame();
context_frame.offset_by(graphics_current_context().draw_offset());
add_clip(context_frame);
set_draw_offset(context_frame.origin());
}
Context(View& view, const LG::Rect& frame, RelativeToCurrentContext)
: Context(view.window()->bitmap())
{
auto context_frame = frame;
context_frame.offset_by(graphics_current_context().draw_offset());
add_clip(context_frame);
set_draw_offset(context_frame.origin());
}
};
} // namespace UI

View File

@@ -0,0 +1,30 @@
#pragma once
#include <libg/Context.h>
namespace UI {
static inline void graphics_push_context(LG::Context&& context)
{
extern std::vector<LG::Context> s_ui_graphics_contexts;
s_ui_graphics_contexts.push_back(std::move(context));
}
static inline void graphics_push_context(const LG::Context& context)
{
extern std::vector<LG::Context> s_ui_graphics_contexts;
s_ui_graphics_contexts.push_back(context);
}
static inline void graphics_pop_context()
{
extern std::vector<LG::Context> s_ui_graphics_contexts;
s_ui_graphics_contexts.pop_back();
}
static inline LG::Context& graphics_current_context()
{
extern std::vector<LG::Context> s_ui_graphics_contexts;
return s_ui_graphics_contexts.back();
}
} // namespace UI

View File

@@ -0,0 +1,98 @@
#pragma once
#include <functional>
#include <libfoundation/Event.h>
#include <libfoundation/EventReceiver.h>
#include <libg/Context.h>
#include <libui/Event.h>
#include <libui/View.h>
#include <libui/Window.h>
namespace details {
typedef std::function<void(UI::View*)> target_func_type;
class CallEvent final : public UI::Event {
public:
friend class Caller;
CallEvent(target_func_type callback, UI::View* view)
: Event(Event::Type::UIHandlerInvoke)
, m_callback(callback)
, m_view(view)
{
}
~CallEvent() = default;
UI::View* view() const { return m_view; }
private:
UI::View* m_view;
target_func_type m_callback;
};
class Caller : public LFoundation::EventReceiver {
public:
friend class EventLoop;
Caller()
: EventReceiver()
{
}
void receive_event(std::unique_ptr<LFoundation::Event> event) override
{
switch (event->type()) {
case UI::Event::Type::UIHandlerInvoke: {
CallEvent& own_event = *(CallEvent*)event.get();
own_event.m_callback(own_event.view());
break;
}
}
}
};
} // namespace details
namespace UI {
class Control : public View {
UI_OBJECT();
public:
struct Target {
details::target_func_type target_func;
UI::Event::Type for_event;
};
~Control() = default;
template <class ActionType>
void add_target(ActionType target_func, UI::Event::Type for_event)
{
m_targets.push_back(Target { target_func, for_event });
}
void send_actions(UI::Event::Type for_event)
{
auto& event_loop = LFoundation::EventLoop::the();
for (auto& target : m_targets) {
if (target.for_event == for_event) {
event_loop.add(m_caller, new details::CallEvent(target.target_func, this));
}
}
}
std::vector<Target>& targets() { return m_targets; }
const std::vector<Target>& targets() const { return m_targets; }
protected:
Control(View* superview, const LG::Rect& r)
: View(superview, r)
{
}
Control(View* superview, Window* window, const LG::Rect& r) = delete;
private:
std::vector<Target> m_targets {};
bool is_selected { false };
bool is_enabled { false };
details::Caller m_caller {};
};
} // namespace UI

View File

@@ -0,0 +1,28 @@
#pragma once
namespace UI {
class EdgeInsets {
public:
EdgeInsets() = default;
EdgeInsets(int top, int left, int bottom, int right)
: m_top(top)
, m_left(left)
, m_bottom(bottom)
, m_right(right)
{
}
inline int top() const { return m_top; }
inline int left() const { return m_left; }
inline int bottom() const { return m_bottom; }
inline int right() const { return m_right; }
private:
int m_top { 0 };
int m_bottom { 0 };
int m_left { 0 };
int m_right { 0 };
};
} // namespace UI

View File

@@ -0,0 +1,383 @@
#pragma once
#include <libapi/window_server/MessageContent/MouseAction.h>
#include <libfoundation/Event.h>
#include <libg/Rect.h>
#include <string>
#include <sys/types.h>
namespace UI {
class Event : public LFoundation::Event {
public:
// Some of events can be sent and are used in processing of data,
// while some part of events are used only for describing actions.
enum Type {
Invalid = 0x2000,
MouseEvent,
MouseUpEvent,
MouseDownEvent,
MouseActionEvent,
MouseEnterEvent,
MouseLeaveEvent,
MouseWheelEvent,
KeyUpEvent,
KeyDownEvent,
DisplayEvent,
LayoutEvent,
WindowCloseRequestEvent,
ResizeEvent,
MenuBarActionEvent,
PopupActionEvent,
UIHandlerInvoke,
NotifyWindowCreateEvent,
NotifyWindowStatusChangedEvent,
NotifyWindowIconChangedEvent,
NotifyWindowTitleChangedEvent,
ViewDidLoad,
Other,
};
explicit Event(int type)
: LFoundation::Event(type)
{
}
~Event() = default;
};
class MouseEvent : public Event {
public:
MouseEvent(uint32_t x, uint32_t y)
: Event(Event::Type::MouseEvent)
, m_x(x)
, m_y(y)
{
}
~MouseEvent() = default;
uint32_t x() const { return m_x; }
uint32_t y() const { return m_y; }
private:
uint32_t m_x;
uint32_t m_y;
};
class MouseActionEvent : public Event {
public:
MouseActionEvent(MouseActionType type, uint32_t x, uint32_t y)
: Event(Event::Type::MouseActionEvent)
, m_type(type)
, m_x(x)
, m_y(y)
{
}
~MouseActionEvent() = default;
MouseActionType type() const { return m_type; }
uint32_t x() const { return m_x; }
uint32_t y() const { return m_y; }
private:
MouseActionType m_type;
uint32_t m_x;
uint32_t m_y;
};
class MouseLeaveEvent : public Event {
public:
MouseLeaveEvent(uint32_t x, uint32_t y)
: Event(Event::Type::MouseLeaveEvent)
, m_x(x)
, m_y(y)
{
}
~MouseLeaveEvent() = default;
uint32_t x() const { return m_x; }
uint32_t y() const { return m_y; }
private:
uint32_t m_x;
uint32_t m_y;
};
class MouseWheelEvent : public Event {
public:
MouseWheelEvent(uint32_t x, uint32_t y, int data)
: Event(Event::Type::MouseWheelEvent)
, m_x(x)
, m_y(y)
, m_wheel_data(data)
{
}
~MouseWheelEvent() = default;
uint32_t x() const { return m_x; }
uint32_t y() const { return m_y; }
int wheel_data() const { return m_wheel_data; }
private:
uint32_t m_x;
uint32_t m_y;
int m_wheel_data;
};
typedef uint32_t key_t;
class KeyUpEvent : public Event {
public:
KeyUpEvent(key_t key)
: Event(Event::Type::KeyUpEvent)
, m_key(key)
{
}
~KeyUpEvent() = default;
key_t key() const { return m_key; }
private:
key_t m_key;
};
class KeyDownEvent : public Event {
public:
KeyDownEvent(key_t key)
: Event(Event::Type::KeyDownEvent)
, m_key(key)
{
}
~KeyDownEvent() = default;
key_t key() const { return m_key; }
private:
key_t m_key;
};
class DisplayEvent : public Event {
public:
DisplayEvent(const LG::Rect& rect)
: Event(Event::Type::DisplayEvent)
, m_display_bounds(rect)
{
}
~DisplayEvent() = default;
LG::Rect& bounds() { return m_display_bounds; }
const LG::Rect& bounds() const { return m_display_bounds; }
private:
LG::Rect m_display_bounds;
};
class View;
class LayoutEvent : public Event {
public:
LayoutEvent(View* rect)
: Event(Event::Type::LayoutEvent)
, m_target(rect)
{
}
~LayoutEvent() = default;
View* target() const { return m_target; }
private:
View* m_target;
};
class WindowCloseRequestEvent : public Event {
public:
WindowCloseRequestEvent(uint32_t window_id)
: Event(Event::Type::WindowCloseRequestEvent)
, m_window_id(window_id)
{
}
~WindowCloseRequestEvent() = default;
uint32_t window_id() const { return m_window_id; }
private:
uint32_t m_window_id;
};
class ResizeEvent : public Event {
public:
ResizeEvent(uint32_t window_id, const LG::Rect& bounds)
: Event(Event::Type::ResizeEvent)
, m_window_id(window_id)
, m_bounds(bounds)
{
}
~ResizeEvent() = default;
uint32_t window_id() const { return m_window_id; }
const LG::Rect& bounds() const { return m_bounds; }
private:
uint32_t m_window_id;
LG::Rect m_bounds;
};
class MenuBarActionEvent : public Event {
public:
MenuBarActionEvent(uint32_t window_id, int menu_id, int item_id)
: Event(Event::Type::MenuBarActionEvent)
, m_window_id(window_id)
, m_menu_id(menu_id)
, m_item_id(item_id)
{
}
~MenuBarActionEvent() = default;
uint32_t window_id() const { return m_window_id; }
int menu_id() const { return m_menu_id; }
int item_id() const { return m_item_id; }
private:
uint32_t m_window_id;
int m_menu_id;
int m_item_id;
};
class PopupActionEvent : public Event {
public:
PopupActionEvent(uint32_t window_id, int menu_id, int item_id)
: Event(Event::Type::PopupActionEvent)
, m_window_id(window_id)
, m_menu_id(menu_id)
, m_item_id(item_id)
{
}
~PopupActionEvent() = default;
uint32_t window_id() const { return m_window_id; }
int menu_id() const { return m_menu_id; }
int item_id() const { return m_item_id; }
private:
uint32_t m_window_id;
int m_menu_id;
int m_item_id;
};
// Notifiers
class NotifyWindowCreateEvent : public Event {
public:
NotifyWindowCreateEvent(std::string&& bundle_id, std::string&& icon_path, uint32_t changed_window_id, int changed_window_type)
: Event(Event::Type::NotifyWindowCreateEvent)
, m_window_id(changed_window_id)
, m_icon_path(icon_path)
, m_bundle_id(bundle_id)
, m_window_type(changed_window_type)
{
}
~NotifyWindowCreateEvent() = default;
uint32_t window_id() const { return m_window_id; }
int window_type() const { return m_window_type; }
const std::string& icon_path() const { return m_icon_path; }
const std::string& bundle_id() const { return m_bundle_id; }
private:
int m_window_id;
int m_window_type;
std::string m_bundle_id;
std::string m_icon_path;
};
class NotifyWindowStatusChangedEvent : public Event {
public:
NotifyWindowStatusChangedEvent(uint32_t changed_window_id, int type)
: Event(Event::Type::NotifyWindowStatusChangedEvent)
, m_changed_window_id(changed_window_id)
, m_type(type)
{
}
~NotifyWindowStatusChangedEvent() = default;
uint32_t changed_window_id() const { return m_changed_window_id; }
int type() const { return m_type; }
private:
uint32_t m_changed_window_id;
int m_type;
};
class NotifyWindowIconChangedEvent : public Event {
public:
NotifyWindowIconChangedEvent(uint32_t changed_window_id, const std::string& path)
: Event(Event::Type::NotifyWindowIconChangedEvent)
, m_changed_window_id(changed_window_id)
, m_icon_path(path)
{
}
NotifyWindowIconChangedEvent(uint32_t changed_window_id, std::string&& path)
: Event(Event::Type::NotifyWindowIconChangedEvent)
, m_changed_window_id(changed_window_id)
, m_icon_path(std::move(path))
{
}
~NotifyWindowIconChangedEvent() = default;
uint32_t changed_window_id() const { return m_changed_window_id; }
const std::string& icon_path() const { return m_icon_path; }
private:
uint32_t m_changed_window_id;
std::string m_icon_path;
};
class NotifyWindowTitleChangedEvent : public Event {
public:
NotifyWindowTitleChangedEvent(uint32_t changed_window_id, const std::string& title)
: Event(Event::Type::NotifyWindowTitleChangedEvent)
, m_changed_window_id(changed_window_id)
, m_title(title)
{
}
NotifyWindowTitleChangedEvent(uint32_t changed_window_id, std::string&& title)
: Event(Event::Type::NotifyWindowTitleChangedEvent)
, m_changed_window_id(changed_window_id)
, m_title(std::move(title))
{
}
~NotifyWindowTitleChangedEvent() = default;
uint32_t changed_window_id() const { return m_changed_window_id; }
const std::string& title() const { return m_title; }
private:
uint32_t m_changed_window_id;
std::string m_title;
};
// View Life Cycle Events
class ViewDidLoadEvent : public Event {
public:
ViewDidLoadEvent()
: Event(Event::Type::ViewDidLoad)
{
}
~ViewDidLoadEvent() = default;
private:
};
} // namespace UI

View File

@@ -0,0 +1,42 @@
#pragma once
#include <libui/GestureRecognizer.h>
namespace UI {
class GestureManager final {
public:
GestureManager() = default;
~GestureManager()
{
for (auto* gst : m_handlers) {
delete gst;
}
}
inline void add(GestureRecognizer* recon) { m_handlers.push_back(recon); }
void mouse_up()
{
for (auto* handler : m_handlers) {
handler->mouse_up();
}
}
void mouse_down(const LG::Point<int>& location)
{
for (auto* handler : m_handlers) {
handler->mouse_down(location);
}
}
void mouse_moved(const LG::Point<int>& new_location)
{
for (auto* handler : m_handlers) {
handler->mouse_moved(new_location);
}
}
private:
std::vector<GestureRecognizer*> m_handlers;
};
} // namespace UI

View File

@@ -0,0 +1,138 @@
#pragma once
#include <functional>
#include <libfoundation/Event.h>
#include <libfoundation/EventLoop.h>
#include <libfoundation/EventReceiver.h>
#include <libui/Event.h>
#include <vector>
namespace UI {
class GestureRecognizer;
}
// TODO: This invoker is really similar to what we can see in Control.h,
// maybe move out them to Inovokers.h or smth like that?
namespace GestureDetails {
typedef std::function<void(const UI::GestureRecognizer*)> target_func_type;
class CallEvent final : public UI::Event {
public:
friend class Caller;
CallEvent(target_func_type callback, const UI::GestureRecognizer* recon)
: Event(Event::Type::UIHandlerInvoke)
, m_callback(callback)
, m_recognizer(recon)
{
}
~CallEvent() = default;
const UI::GestureRecognizer* recognizer() const { return m_recognizer; }
private:
const UI::GestureRecognizer* m_recognizer;
target_func_type m_callback;
};
class Caller : public LFoundation::EventReceiver {
public:
friend class EventLoop;
Caller()
: EventReceiver()
{
}
void receive_event(std::unique_ptr<LFoundation::Event> event) override
{
switch (event->type()) {
case UI::Event::Type::UIHandlerInvoke: {
CallEvent& own_event = *(CallEvent*)event.get();
own_event.m_callback(own_event.recognizer());
break;
}
}
}
};
} // namespace details
namespace UI {
class GestureRecognizer {
public:
enum State {
Possible,
Began,
Cancelled,
Ended,
};
GestureRecognizer(GestureDetails::target_func_type target)
: m_target(target)
{
}
virtual ~GestureRecognizer() = default;
State state() const { return m_state; }
virtual void mouse_up() { }
virtual void mouse_down(const LG::Point<int>&) { }
virtual void mouse_moved(const LG::Point<int>&) { }
protected:
void update_state(State newstate)
{
m_state = newstate;
did_update_state();
}
void did_update_state()
{
LFoundation::EventLoop::the().add(m_caller, new GestureDetails::CallEvent(m_target, this));
}
private:
State m_state { State::Ended };
GestureDetails::target_func_type m_target;
GestureDetails::Caller m_caller {};
};
class SwipeGestureRecognizer final : public GestureRecognizer {
public:
SwipeGestureRecognizer(GestureDetails::target_func_type target)
: GestureRecognizer(target)
{
}
~SwipeGestureRecognizer() = default;
virtual void mouse_up() override
{
if (m_was_moved && m_was_down) {
update_state(State::Ended);
} else {
update_state(State::Cancelled);
}
m_was_down = false;
m_was_moved = false;
}
virtual void mouse_down(const LG::Point<int>&) override
{
m_was_down = true;
update_state(State::Possible);
}
virtual void mouse_moved(const LG::Point<int>&) override
{
if (state() == State::Possible && m_was_down) {
update_state(State::Began);
m_was_moved = true;
}
}
private:
bool m_was_moved { false };
bool m_was_down { false };
};
} // namespace UI

View File

@@ -0,0 +1,53 @@
#pragma once
#include <libg/Font.h>
#include <libui/Constants/Text.h>
#include <libui/EdgeInsets.h>
#include <libui/View.h>
#include <string>
namespace UI {
class Label : public View {
UI_OBJECT();
public:
~Label() = default;
void set_text(const std::string& text) { m_text = text, set_needs_display(); }
void set_text(std::string&& text) { m_text = std::move(text), set_needs_display(); }
const std::string& text() const { return m_text; }
void set_text_color(const LG::Color& color) { m_text_color = color; }
const LG::Color& text_color() const { return m_text_color; }
void set_content_edge_insets(const EdgeInsets& ei) { m_content_edge_insets = ei; }
const EdgeInsets& content_edge_insets() const { return m_content_edge_insets; }
void set_alignment(Text::Alignment alignment) { m_alignment = alignment; }
Text::Alignment alignment() const { return m_alignment; }
void set_font(const LG::Font& font) { m_font = font, set_needs_display(); }
inline const LG::Font& font() const { return m_font; }
inline size_t preferred_width() const { return text_width() + m_content_edge_insets.left() + m_content_edge_insets.right(); }
virtual void display(const LG::Rect& rect) override;
protected:
Label(View* superview, const LG::Rect&);
Label(View* superview, Window* window, const LG::Rect&);
private:
void recalc_bounds();
size_t text_height() const;
size_t text_width() const;
std::string m_text {};
LG::Color m_text_color { LG::Color::Black };
LG::Font m_font { LG::Font::system_font() };
Text::Alignment m_alignment { Text::Alignment::Left };
EdgeInsets m_content_edge_insets {};
};
} // namespace UI

View File

@@ -0,0 +1,34 @@
#pragma once
#include <libg/Font.h>
#include <libui/Constants/Text.h>
#include <libui/EdgeInsets.h>
#include <libui/View.h>
#include <string>
namespace UI {
class Layer {
public:
Layer() = default;
~Layer() = default;
void set_corner_mask(const LG::CornerMask& cm) { m_corner_mask = cm; }
const LG::CornerMask& corner_mask() const { return m_corner_mask; }
void set_shading(const LG::Shading& sh) { m_shading = sh; }
const LG::Shading& shading() const { return m_shading; }
void display(const LG::Rect& rect, const LG::Rect& frame) const
{
LG::Context ctx = graphics_current_context();
ctx.set_fill_color(LG::Color(100, 100, 100, 40));
ctx.add_clip(rect);
ctx.draw_box_shading(frame, m_shading, m_corner_mask);
}
private:
LG::CornerMask m_corner_mask {};
LG::Shading m_shading {};
};
} // namespace UI

View File

@@ -0,0 +1,31 @@
#pragma once
#include <libapi/window_server/MessageContent/MouseAction.h>
#include <libui/Common/MenuItem.h>
#include <string>
#include <utility>
namespace UI {
class Window;
class MenuBar {
friend class Window;
public:
MenuBar() = default;
~MenuBar() = default;
void add_menu(const Menu&);
void add_menu(Menu&&);
std::vector<Menu>& menus() { return m_menus; }
const std::vector<Menu>& menus() const { return m_menus; }
private:
void set_host_window_id(int win_id) { m_host_window_id = win_id; }
int host_window_id() const { return m_host_window_id; }
int m_host_window_id { -1 };
std::vector<Menu> m_menus;
};
} // namespace UI

View File

@@ -0,0 +1,29 @@
#pragma once
#include <libui/Common/MenuItem.h>
#include <string>
#include <utility>
namespace UI {
class Window;
class PopupMenu {
friend class Window;
public:
PopupMenu() = default;
~PopupMenu() = default;
void show(LG::Point<int>, const Menu& menu);
void show(LG::Point<int>, Menu&& menu);
Menu& menu() { return m_menu; }
const Menu& menu() const { return m_menu; }
private:
void set_host_window_id(int win_id) { m_host_window_id = win_id; }
Menu m_menu;
uint32_t m_host_window_id;
};
} // namespace UI

View File

@@ -0,0 +1,32 @@
#pragma once
#include <libfoundation/Object.h>
#include <libg/Rect.h>
#include <libui/Event.h>
namespace UI {
class Window;
class Responder : public LFoundation::Object {
public:
bool send_invalidate_message_to_server(const LG::Rect& rect) const;
void send_display_message_to_self(Window& win, const LG::Rect& display_rect);
void send_layout_message(Window& win, UI::View* for_view);
void receive_event(std::unique_ptr<LFoundation::Event> event) override;
virtual void receive_mouse_move_event(MouseEvent&) { }
virtual void receive_mouse_action_event(MouseActionEvent&) { }
virtual void receive_mouse_leave_event(MouseLeaveEvent&) { }
virtual bool receive_mouse_wheel_event(MouseWheelEvent&) { return false; }
virtual void receive_keyup_event(KeyUpEvent&) { }
virtual void receive_keydown_event(KeyDownEvent&) { }
virtual void receive_display_event(DisplayEvent&) { m_display_message_sent = false; }
virtual bool receive_layout_event(const LayoutEvent&, bool force_layout_if_not_target = false) { return false; }
protected:
bool m_display_message_sent { false };
LG::Rect m_prev_display_message {};
Responder() = default;
};
} // namespace UI

View File

@@ -0,0 +1,50 @@
#pragma once
#include <cstring>
#include <fcntl.h>
#include <libg/Rect.h>
#include <string>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <utility>
namespace UI {
class Screen;
static Screen* __main_screen;
class Screen {
public:
Screen()
: m_screen_fd(open("/dev/bga", O_RDONLY))
, m_bounds(0, 0, ioctl(m_screen_fd, BGA_GET_WIDTH, 0), ioctl(m_screen_fd, BGA_GET_HEIGHT, 0))
, m_scale(ioctl(m_screen_fd, BGA_GET_SCALE, 0))
{
}
explicit Screen(const std::string& path)
: m_screen_fd(open(path.c_str(), O_RDONLY))
, m_bounds(0, 0, ioctl(m_screen_fd, BGA_GET_WIDTH, 0), ioctl(m_screen_fd, BGA_GET_HEIGHT, 0))
, m_scale(ioctl(m_screen_fd, BGA_GET_SCALE, 0))
{
}
~Screen() = default;
int scale() const { return m_scale; }
const LG::Rect& bounds() const { return m_bounds; }
static Screen& main()
{
if (!__main_screen) {
__main_screen = new UI::Screen();
}
return *__main_screen;
}
private:
int m_screen_fd;
int m_scale { 1 };
LG::Rect m_bounds;
};
} // namespace UI

View File

@@ -0,0 +1,57 @@
#pragma once
#include <libg/Size.h>
#include <libui/Constants/Layout.h>
#include <libui/EdgeInsets.h>
#include <libui/View.h>
#include <string>
#include <utility>
namespace UI {
class ScrollView : public View {
UI_OBJECT();
public:
~ScrollView() = default;
inline const LG::Size& content_size() const { return m_content_size; }
inline LG::Size& content_size() { return m_content_size; }
inline const LG::Point<int>& content_offset() const { return m_content_offset; }
inline LG::Point<int>& content_offset() { return m_content_offset; }
virtual std::optional<View*> subview_at(const LG::Point<int>& point) const override;
virtual void display(const LG::Rect& rect) override;
virtual WheelEventResponse mouse_wheel_event(int wheel_data) override;
virtual void receive_mouse_move_event(MouseEvent&) override;
virtual void receive_display_event(DisplayEvent&) override;
protected:
ScrollView(View* superview, const LG::Rect&);
ScrollView(View* superview, Window* window, const LG::Rect& frame);
void display_scroll_indicators(LG::Context&);
// The location of a subview relativly to its superview could
// differ from it's frame() (e.g when scrolling), to determine
// the right location we ask the superview to return it.
virtual LG::Point<int> subview_location(const View& subview) const override;
private:
void setup_scroll_animation(int wheel_data);
void do_scroll_animation_step();
void rearm_scroll_animation();
void do_scroll(int n_x, int n_y);
void recalc_content_props();
LG::Size m_content_size {};
LG::Point<int> m_content_offset {};
LG::Point<int> m_mouse_location {};
bool m_has_timer { false };
size_t m_scroll_velocity { 0 };
size_t m_last_scroll_multiplier { 0 };
LG::Point<int> m_animation_target { 0, 0 };
};
} // namespace UI

View File

@@ -0,0 +1,78 @@
#pragma once
#include <libg/Font.h>
#include <libui/Constants/Layout.h>
#include <libui/EdgeInsets.h>
#include <libui/View.h>
#include <string>
#include <utility>
namespace UI {
class StackView : public View {
UI_OBJECT();
public:
enum class Distribution {
Standard,
FillEqually,
EqualSpacing,
EqualCentering,
};
enum class Alignment {
Leading,
Center,
Trailing,
};
~StackView() = default;
template <class T, class... Args>
T& add_arranged_subview(Args&&... args)
{
T& view = add_subview<T>(LG::Rect(0, 0, 0, 0), std::forward<Args>(args)...);
m_views.push_back(&view);
return view;
}
const std::vector<View*>& arranged_subviews() const { return m_views; }
std::vector<View*>& arranged_subviews() { return m_views; }
void set_axis(LayoutConstraints::Axis axis) { m_axis = axis; }
LayoutConstraints::Axis axis() const { return m_axis; }
void set_distribution(Distribution dist) { m_distribution = dist; }
Distribution distribution() const { return m_distribution; }
void set_alignment(Alignment alignment) { m_alignment = alignment; }
Alignment alignment() const { return m_alignment; }
void set_spacing(size_t spacing) { m_spacing = spacing; }
size_t spacing() const { return m_spacing; }
virtual bool receive_layout_event(const LayoutEvent&, bool force_layout_if_not_target = false) override;
protected:
StackView(View* superview, const LG::Rect&);
StackView(View* superview, Window* window, const LG::Rect& frame);
private:
void recalc_subviews_positions();
void recalc_fill_equally();
size_t recalc_total_content_width();
size_t recalc_total_content_height();
size_t recalc_subview_min_x(View*);
size_t recalc_subview_min_y(View*);
size_t recalc_spacing();
size_t recalc_initial_spacing(size_t element_spacing);
size_t recalc_equal_spacing_horizontal() { return m_views.size() > 2 ? (bounds().width() - recalc_total_content_width()) / (m_views.size() - 1) : 0; }
size_t recalc_equal_spacing_vertical() { return m_views.size() > 2 ? (bounds().height() - recalc_total_content_height()) / (m_views.size() - 1) : 0; }
std::vector<View*> m_views;
size_t m_spacing { 0 };
Alignment m_alignment { Alignment::Leading };
Distribution m_distribution { Distribution::Standard };
LayoutConstraints::Axis m_axis { LayoutConstraints::Axis::Horizontal };
};
} // namespace UI

View File

@@ -0,0 +1,60 @@
#pragma once
#include <libg/Font.h>
#include <libui/Constants/Text.h>
#include <libui/Control.h>
#include <libui/EdgeInsets.h>
#include <string>
namespace UI {
class TextField : public Control {
UI_OBJECT();
public:
enum Type {
System,
Custom,
};
~TextField() = default;
const std::string& text() const { return m_text; }
void set_text(const std::string& text) { m_text = text, set_needs_display(); }
void set_text(std::string&& text) { m_text = std::move(text), set_needs_display(); }
void set_placeholder_text(const std::string& text) { m_placeholder_text = text, set_needs_display(); }
void set_placeholder_text(std::string&& text) { m_placeholder_text = std::move(text), set_needs_display(); }
void set_text_color(const LG::Color& color) { m_text_color = color; }
const LG::Color& text_color() const { return m_text_color; }
void set_content_edge_insets(const EdgeInsets& ei) { m_content_edge_insets = ei; }
const EdgeInsets& content_edge_insets() const { return m_content_edge_insets; }
void set_font(const LG::Font& font) { m_font = font, set_needs_display(); }
inline const LG::Font& font() const { return m_font; }
virtual void display(const LG::Rect& rect) override;
virtual void mouse_entered(const LG::Point<int>& location) override;
virtual void mouse_exited() override;
virtual void mouse_down(const LG::Point<int>& location) override;
virtual void mouse_up() override;
protected:
TextField(View* superview, const LG::Rect& frame);
private:
size_t text_width(const std::string& text);
size_t text_height(const std::string& text) const;
std::string m_text {};
std::string m_placeholder_text {};
LG::Color m_text_color { LG::Color::DarkSystemText };
LG::Font m_font { LG::Font::system_font() };
Text::Alignment m_alignment { Text::Alignment::Left };
EdgeInsets m_content_edge_insets { 12, 12, 12, 12 };
};
} // namespace UI

View File

@@ -0,0 +1,43 @@
#pragma once
#include <libg/Size.h>
#include <libui/Constants/Layout.h>
#include <libui/EdgeInsets.h>
#include <libui/ScrollView.h>
#include <string>
#include <utility>
namespace UI {
class TextView : public ScrollView {
UI_OBJECT();
public:
~TextView() = default;
void set_text(const std::string& text) { m_text = text, recalc_text_size(), set_needs_display(); }
void set_text(std::string&& text) { m_text = std::move(text), recalc_text_size(), set_needs_display(); }
const std::string& text() const { return m_text; }
void set_text_color(const LG::Color& color) { m_text_color = color, set_needs_display(); }
const LG::Color& text_color() const { return m_text_color; }
void set_font(const LG::Font& font) { m_font = font, recalc_text_size(), set_needs_display(); }
inline const LG::Font& font() const { return m_font; }
virtual void display(const LG::Rect& rect) override;
virtual void mouse_entered(const LG::Point<int>& location) override;
virtual void mouse_exited() override;
protected:
TextView(View* superview, const LG::Rect&);
TextView(View* superview, Window* window, const LG::Rect& frame);
private:
void recalc_text_size();
std::string m_text {};
LG::Color m_text_color { LG::Color::Black };
LG::Font m_font { LG::Font::system_font() };
};
} // namespace UI

View File

@@ -0,0 +1,277 @@
#pragma once
#include <libfoundation/Logger.h>
#include <libg/Color.h>
#include <libg/Point.h>
#include <libg/Rect.h>
#include <libui/Constraint.h>
#include <libui/ContextManager.h>
#include <libui/EdgeInsets.h>
#include <libui/GestureManager.h>
#include <libui/Layer.h>
#include <libui/Responder.h>
#include <list>
#include <optional>
#include <utility>
#include <vector>
#define UI_OBJECT() friend class View
namespace UI {
struct SafeArea {
static const int Top = 8;
#ifdef TARGET_DESKTOP
static const int Bottom = 8;
#elif TARGET_MOBILE
static const int Bottom = 20;
#endif
static const int Left = 8;
static const int Right = 8;
};
struct Padding {
static const int System = 8;
static const int AfterTitle = 12;
};
class Window;
class View : public Responder {
public:
friend class Window;
~View();
template <class T, class... Args>
T& add_subview(Args&&... args)
{
T* subview = new T(this, std::forward<Args>(args)...);
m_subviews.push_back(subview);
did_add_subview(*subview);
return *subview;
}
virtual void did_add_subview(View& view) { }
void remove_from_superview();
template <typename Callback>
void foreach_subview(Callback callback) const
{
for (auto* viewptr : m_subviews) {
if (!callback(*viewptr)) {
return;
}
}
}
virtual std::optional<View*> subview_at(const LG::Point<int>& point) const;
View& hit_test(const LG::Point<int>& point);
inline const LG::Rect& frame() const { return m_frame; }
inline const LG::Rect& bounds() const { return m_bounds; }
inline LG::Rect& frame() { return m_frame; }
inline LG::Rect& bounds() { return m_bounds; }
inline LG::Point<int> center() { return LG::Point<int>(frame().mid_x(), frame().mid_y()); }
inline void set_width(size_t x) { m_frame.set_width(x), m_bounds.set_width(x), set_needs_display(); }
inline void set_height(size_t x) { m_frame.set_height(x), m_bounds.set_height(x), set_needs_display(); }
inline void turn_on_constraint_based_layout(bool b) { m_constraint_based_layout = b; }
void add_constraint(const Constraint& constraint) { m_constrints.push_back(constraint); }
const std::vector<UI::Constraint>& constraints() const { return m_constrints; }
template <class RecognizerT, class ActionType>
inline void add_gesture_recognizer(ActionType m_target)
{
RecognizerT* recognizer = new RecognizerT(m_target);
m_gesture_manager.add(recognizer);
}
virtual void layout_subviews();
inline void set_needs_layout()
{
send_layout_message(*window(), this);
set_needs_display();
}
LG::Rect frame_in_window();
inline Window* window() { return m_window; }
inline bool has_superview() { return m_superview; }
inline View* superview() { return m_superview; }
inline std::list<View*>& subviews() { return m_subviews; }
inline const std::list<View*>& subviews() const { return m_subviews; }
void set_needs_display(const LG::Rect&);
inline void set_needs_display() { set_needs_display(bounds()); }
inline bool is_hovered() const { return m_hovered; }
inline bool is_active() const { return m_active; }
inline void set_focusable(bool val) { m_focusable = val; }
inline bool is_focusable() const { return m_focusable; }
inline Layer& layer() { return m_layer; }
inline const Layer& layer() const { return m_layer; }
virtual void display(const LG::Rect& rect);
virtual void did_display(const LG::Rect& rect);
virtual void mouse_moved(const LG::Point<int>& new_location);
virtual void mouse_entered(const LG::Point<int>& location);
virtual void mouse_exited();
// Return
enum WheelEventResponse {
Skipped,
Handled,
};
virtual WheelEventResponse mouse_wheel_event(int wheel_data);
virtual void mouse_down(const LG::Point<int>& location);
virtual void mouse_up();
virtual void receive_mouse_move_event(MouseEvent&) override;
virtual void receive_mouse_action_event(MouseActionEvent&) override;
virtual void receive_mouse_leave_event(MouseLeaveEvent&) override;
virtual bool receive_mouse_wheel_event(MouseWheelEvent&) override;
virtual void receive_keyup_event(KeyUpEvent&) override;
virtual void receive_keydown_event(KeyDownEvent&) override;
virtual void receive_display_event(DisplayEvent&) override;
virtual bool receive_layout_event(const LayoutEvent&, bool force_layout_if_not_target = false) override;
inline LG::Color& background_color() { return m_background_color; }
inline const LG::Color& background_color() const { return m_background_color; }
inline void set_background_color(const LG::Color& background_color) { m_background_color = background_color, set_needs_display(); }
protected:
View(View* superview, const LG::Rect&);
View(View* superview, Window* window, const LG::Rect&);
inline void set_hovered(bool value) { m_hovered = value; }
inline void set_active(bool value) { m_active = value; }
virtual LG::Point<int> subview_location(const View& subview) const;
template <Constraint::Attribute attr>
inline void add_interpreted_constraint_to_mask() { m_applied_constraints_mask |= (1 << (int)attr); }
template <Constraint::Attribute attr>
inline bool has_interpreted_constraint_in_mask() { return (m_applied_constraints_mask & (1 << (int)attr)); }
inline void constraint_interpreter(const Constraint& constraint);
private:
void set_window(Window* window) { m_window = window; }
void set_superview(View* superview) { m_superview = superview; }
void remove_view(View* view);
View* m_superview { nullptr };
Window* m_window { nullptr };
std::list<View*> m_subviews;
LG::Rect m_frame;
LG::Rect m_bounds;
bool m_constraint_based_layout { false };
std::vector<Constraint> m_constrints {};
uint32_t m_applied_constraints_mask { 0 }; // Constraints applied to this view;
bool m_active { false };
bool m_hovered { false };
bool m_focusable { false };
LG::Color m_background_color { LG::Color::White };
Layer m_layer {};
GestureManager m_gesture_manager;
};
inline void View::constraint_interpreter(const Constraint& constraint)
{
auto get_rel_item_attribute = [&]() {
// Constraints could be added between a view and the view's superview or another view with the same superview.
//
// If it's the case view and the view's superview, it has to take attributes from bound to calculate the right
// view's posistion within this superview.
if (constraint.rel_item() == constraint.item()->superview()) {
return Constraint::get_attribute<int>(constraint.rel_item()->bounds(), constraint.rel_attribute());
} else {
return Constraint::get_attribute<int>(constraint.rel_item()->frame(), constraint.rel_attribute());
}
};
auto calc_new_value = [&]() {
return constraint.multiplier() * get_rel_item_attribute() + constraint.constant();
};
switch (constraint.attribute()) {
case Constraint::Attribute::Top:
Constraint::set_attribute<Constraint::Attribute::Top>(constraint.item()->frame(), calc_new_value());
constraint.item()->add_interpreted_constraint_to_mask<Constraint::Attribute::Top>();
return;
case Constraint::Attribute::Bottom: {
uint32_t value = calc_new_value();
if (!constraint.rel_item()) {
value = constraint.item()->superview()->bounds().max_y() - constraint.constant();
}
if (constraint.item()->has_interpreted_constraint_in_mask<Constraint::Attribute::Top>()) {
uint32_t res = value - constraint.item()->frame().min_y();
Constraint::set_attribute<Constraint::Attribute::Height>(constraint.item()->frame(), res);
Constraint::set_attribute<Constraint::Attribute::Height>(constraint.item()->bounds(), res);
} else {
Constraint::set_attribute<Constraint::Attribute::Bottom>(constraint.item()->frame(), value);
}
constraint.item()->add_interpreted_constraint_to_mask<Constraint::Attribute::Bottom>();
return;
}
case Constraint::Attribute::Left:
Constraint::set_attribute<Constraint::Attribute::Left>(constraint.item()->frame(), calc_new_value());
constraint.item()->add_interpreted_constraint_to_mask<Constraint::Attribute::Left>();
return;
case Constraint::Attribute::Right: {
uint32_t value = calc_new_value();
if (!constraint.rel_item()) {
value = constraint.item()->superview()->bounds().max_x() - constraint.constant();
}
if (constraint.item()->has_interpreted_constraint_in_mask<Constraint::Attribute::Left>()) {
uint32_t res = value - constraint.item()->frame().min_x();
Constraint::set_attribute<Constraint::Attribute::Width>(constraint.item()->frame(), res);
Constraint::set_attribute<Constraint::Attribute::Width>(constraint.item()->bounds(), res);
} else {
Constraint::set_attribute<Constraint::Attribute::Right>(constraint.item()->frame(), value);
}
constraint.item()->add_interpreted_constraint_to_mask<Constraint::Attribute::Right>();
return;
}
case Constraint::Attribute::CenterX:
Constraint::set_attribute<Constraint::Attribute::CenterX>(constraint.item()->frame(), calc_new_value());
constraint.item()->add_interpreted_constraint_to_mask<Constraint::Attribute::CenterX>();
return;
case Constraint::Attribute::CenterY:
Constraint::set_attribute<Constraint::Attribute::CenterY>(constraint.item()->frame(), calc_new_value());
constraint.item()->add_interpreted_constraint_to_mask<Constraint::Attribute::CenterY>();
return;
case Constraint::Attribute::Width:
Constraint::set_attribute<Constraint::Attribute::Width>(constraint.item()->frame(), calc_new_value());
Constraint::set_attribute<Constraint::Attribute::Width>(constraint.item()->bounds(), calc_new_value());
constraint.item()->add_interpreted_constraint_to_mask<Constraint::Attribute::Width>();
return;
case Constraint::Attribute::Height:
Constraint::set_attribute<Constraint::Attribute::Height>(constraint.item()->frame(), calc_new_value());
Constraint::set_attribute<Constraint::Attribute::Height>(constraint.item()->bounds(), calc_new_value());
constraint.item()->add_interpreted_constraint_to_mask<Constraint::Attribute::Height>();
return;
default:
break;
}
}
} // namespace UI

View File

@@ -0,0 +1,47 @@
#pragma once
#include <libfoundation/Event.h>
#include <libfoundation/EventLoop.h>
#include <libfoundation/EventReceiver.h>
#include <libui/View.h>
#include <memory>
#include <sys/types.h>
namespace UI {
class BaseViewController : public LFoundation::EventReceiver {
public:
BaseViewController() = default;
virtual ~BaseViewController() = default;
virtual void view_did_load() { }
virtual void receive_event(std::unique_ptr<LFoundation::Event> event) override
{
switch (event->type()) {
case Event::Type::ViewDidLoad:
view_did_load();
break;
}
}
private:
};
template <class ViewT>
class ViewController : public BaseViewController {
public:
ViewController(ViewT& view)
: m_view(view)
{
}
virtual ~ViewController() = default;
ViewT& view() { return m_view; }
const ViewT& view() const { return m_view; }
Window& window() { return *m_view.window(); }
private:
ViewT& m_view;
};
} // namespace UI

View File

@@ -0,0 +1,104 @@
#pragma once
#include <libapi/window_server/Connections/WSConnection.h>
#include <libapi/window_server/MessageContent/MenuBar.h>
#include <libapi/window_server/MessageContent/Window.h>
#include <libfoundation/Event.h>
#include <libfoundation/EventReceiver.h>
#include <libfoundation/SharedBuffer.h>
#include <libg/Color.h>
#include <libg/PixelBitmap.h>
#include <libg/Size.h>
#include <libui/MenuBar.h>
#include <libui/PopupMenu.h>
#include <libui/View.h>
#include <libui/ViewController.h>
#include <string>
#include <sys/types.h>
namespace UI {
using ::WindowStatusUpdateType;
using ::WindowType;
class Connection;
class Window : public LFoundation::EventReceiver {
UI_OBJECT();
friend Connection;
public:
Window(const std::string& title, const LG::Size& size, WindowType type = WindowType::Standard);
Window(const std::string& title, const LG::Size& size, const std::string& path);
Window(const std::string& title, const LG::Size& size, const std::string& path, const StatusBarStyle& style);
int id() const { return m_id; }
inline WindowType type() const { return m_type; }
inline const LG::Rect& bounds() const { return m_bounds; }
LFoundation::SharedBuffer<LG::Color>& buffer() { return m_buffer; }
const LFoundation::SharedBuffer<LG::Color>& buffer() const { return m_buffer; }
LG::PixelBitmap& bitmap() { return m_bitmap; }
const LG::PixelBitmap& bitmap() const { return m_bitmap; }
inline void set_bitmap_format(LG::PixelBitmapFormat format) { m_bitmap.set_format(format), did_format_change(); }
template <class ViewT, class ViewControllerT, class... Args>
inline ViewT& create_superview(Args&&... args)
{
ViewT* new_view = new ViewT(nullptr, this, bounds(), args...);
m_superview = new_view;
m_root_view_controller = new ViewControllerT(*new_view);
setup_superview();
m_superview->set_needs_display();
LFoundation::EventLoop::the().add(*m_root_view_controller, new ViewDidLoadEvent());
return *new_view;
}
inline View* superview() const { return m_superview; }
inline void set_focused_view(View& view) { m_focused_view = &view; }
inline View* focused_view() { return m_focused_view; }
MenuBar& menubar() { return m_menubar; }
PopupMenu& popup_manager() { return m_popup; }
bool set_title(const std::string& title);
bool set_status_bar_style(StatusBarStyle style);
bool did_format_change();
bool did_buffer_change();
inline const std::string& title() const { return m_title; }
inline const std::string& icon_path() const { return m_icon_path; }
inline const StatusBarStyle& status_bar_style() const { return m_status_bar_style; }
void receive_event(std::unique_ptr<LFoundation::Event> event) override;
private:
void init_window(const std::string& title, const LG::Size& size, const std::string& icon_path, const StatusBarStyle& style, WindowType type);
void resize(ResizeEvent&);
void setup_superview();
void fill_with_opaque(const LG::Rect&);
inline const LG::Rect& native_bounds() const { return m_native_bounds; }
uint32_t m_id;
BaseViewController* m_root_view_controller { nullptr };
View* m_superview { nullptr };
View* m_focused_view { nullptr };
LG::Rect m_bounds;
LG::Rect m_native_bounds;
LG::PixelBitmap m_bitmap;
LFoundation::SharedBuffer<LG::Color> m_buffer;
std::string m_title { "" };
std::string m_icon_path { "/res/icons/apps/missing.icon" };
LG::Color m_color;
StatusBarStyle m_status_bar_style;
WindowType m_type { WindowType::Standard };
int m_scale { 1 };
MenuBar m_menubar;
PopupMenu m_popup;
};
} // namespace UI