Squash commits for public release
This commit is contained in:
54
libs/libui/include/libui/App.h
Normal file
54
libs/libui/include/libui/App.h
Normal 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
|
||||
43
libs/libui/include/libui/AppDelegate.h
Normal file
43
libs/libui/include/libui/AppDelegate.h
Normal 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
|
||||
64
libs/libui/include/libui/Button.h
Normal file
64
libs/libui/include/libui/Button.h
Normal 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
|
||||
36
libs/libui/include/libui/ClientDecoder.h
Normal file
36
libs/libui/include/libui/ClientDecoder.h
Normal 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
|
||||
74
libs/libui/include/libui/CollectionView.h
Normal file
74
libs/libui/include/libui/CollectionView.h
Normal 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
|
||||
75
libs/libui/include/libui/Common/MenuItem.h
Normal file
75
libs/libui/include/libui/Common/MenuItem.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
37
libs/libui/include/libui/Connection.h
Normal file
37
libs/libui/include/libui/Connection.h
Normal 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
|
||||
10
libs/libui/include/libui/Constants/Layout.h
Normal file
10
libs/libui/include/libui/Constants/Layout.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace UI::LayoutConstraints {
|
||||
|
||||
enum class Axis {
|
||||
Horizontal,
|
||||
Vertical
|
||||
};
|
||||
|
||||
} // namespace UI::LayoutConstraints
|
||||
11
libs/libui/include/libui/Constants/Text.h
Normal file
11
libs/libui/include/libui/Constants/Text.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace UI::Text {
|
||||
|
||||
enum class Alignment {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
};
|
||||
|
||||
} // namespace UI::Text
|
||||
132
libs/libui/include/libui/Constraint.h
Normal file
132
libs/libui/include/libui/Constraint.h
Normal 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
|
||||
52
libs/libui/include/libui/Context.h
Normal file
52
libs/libui/include/libui/Context.h
Normal 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
|
||||
30
libs/libui/include/libui/ContextManager.h
Normal file
30
libs/libui/include/libui/ContextManager.h
Normal 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
|
||||
98
libs/libui/include/libui/Control.h
Normal file
98
libs/libui/include/libui/Control.h
Normal 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
|
||||
28
libs/libui/include/libui/EdgeInsets.h
Normal file
28
libs/libui/include/libui/EdgeInsets.h
Normal 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
|
||||
383
libs/libui/include/libui/Event.h
Normal file
383
libs/libui/include/libui/Event.h
Normal 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
|
||||
42
libs/libui/include/libui/GestureManager.h
Normal file
42
libs/libui/include/libui/GestureManager.h
Normal 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
|
||||
138
libs/libui/include/libui/GestureRecognizer.h
Normal file
138
libs/libui/include/libui/GestureRecognizer.h
Normal 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
|
||||
53
libs/libui/include/libui/Label.h
Normal file
53
libs/libui/include/libui/Label.h
Normal 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
|
||||
34
libs/libui/include/libui/Layer.h
Normal file
34
libs/libui/include/libui/Layer.h
Normal 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
|
||||
31
libs/libui/include/libui/MenuBar.h
Normal file
31
libs/libui/include/libui/MenuBar.h
Normal 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
|
||||
29
libs/libui/include/libui/PopupMenu.h
Normal file
29
libs/libui/include/libui/PopupMenu.h
Normal 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
|
||||
32
libs/libui/include/libui/Responder.h
Normal file
32
libs/libui/include/libui/Responder.h
Normal 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
|
||||
50
libs/libui/include/libui/Screen.h
Normal file
50
libs/libui/include/libui/Screen.h
Normal 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
|
||||
57
libs/libui/include/libui/ScrollView.h
Normal file
57
libs/libui/include/libui/ScrollView.h
Normal 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
|
||||
78
libs/libui/include/libui/StackView.h
Normal file
78
libs/libui/include/libui/StackView.h
Normal 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
|
||||
60
libs/libui/include/libui/TextField.h
Normal file
60
libs/libui/include/libui/TextField.h
Normal 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
|
||||
43
libs/libui/include/libui/TextView.h
Normal file
43
libs/libui/include/libui/TextView.h
Normal 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
|
||||
277
libs/libui/include/libui/View.h
Normal file
277
libs/libui/include/libui/View.h
Normal 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
|
||||
47
libs/libui/include/libui/ViewController.h
Normal file
47
libs/libui/include/libui/ViewController.h
Normal 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
|
||||
104
libs/libui/include/libui/Window.h
Normal file
104
libs/libui/include/libui/Window.h
Normal 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
|
||||
Reference in New Issue
Block a user