#ifndef ui_hpp #define ui_hpp #include "app.hpp" #include "maths.hpp" #include "video.hpp" #include struct Arena; struct UI { struct Vertex { float x, y, u, v, r, g, b, a; }; struct Colour { uint8_t r, g, b, a; Colour(unsigned rgb, uint8_t a = 0xff); float r_f() { return (float)r / 255.0f; }; float g_f() { return (float)g / 255.0f; }; float b_f() { return (float)b / 255.0f; }; float a_f() { return (float)a / 255.0f; }; }; struct Rect { int x, y, w, h; Rect() = default; Rect(int x, int y, int w, int h); void clip(const Rect& other); void shrink(int a); bool contains(int px, int py); }; struct Vertex_Buffer { Rect clip; bool dirty; Staged_Buffer buf; Buffer_Id indices; int start, usage; void init(Device* dev); void init_indices(Device* dev); void destroy(UI* ui); void update_buffer(Context& ctx); void reset(const Rect& clip); void set_clip(const Rect& clip); void add_quad( UI* ui, int x, int y, int w, int h, float u0, float v0, float u1, float v1, Colour col ); void add_rect( UI* ui, int x, int y, int w, int h, Colour col ); void add_char(UI* ui, int x, int y, char ch, Colour col); void add_text( UI* ui, int x, int y, const char* txt, Colour col ); void draw( UI* ui, Context& ctx, Pipeline& pip, Render_Pass& rp ); Vertex_Buffer* next; }; struct Element; Heap* heap; Device* device; const App* app; Texture_Id atlas; Shader_Id shader; Vertex_Format_Id vertex_format; Sampler_Id sampler; Staged_Buffer cbuffer; Vertex_Buffer mesh; Pipeline* pipeline; Render_Pass* render_pass; Element* root, * hot, * hovered; Rect area; bool layout_dirty; struct UI_CBuffer { m4f projection; }; struct { int vert_binding; int atlas_binding; int config_binding; } shader_info; static int text_width(const char* t); static int text_height(const char* t); static UI* create( Device* dev, const App* app, Arena* a, Shader_Id sh ); void init( Device* dev, const App* app, Heap* h, Texture_Id atlas, Shader_Id sh ); void destroy(); void layout(int w, int h); void update(Arena* s); void render(Arena* s, Texture_Id target); void draw_container(const Rect& r, Colour c); void draw_containeri(const Rect& r, Colour c); Element* alloc_element(size_t size); template T* create_element(Element* parent, Args... args) { T* e = (T*)alloc_element(sizeof(T)); new (e) T(this, parent, args...); return e; } struct Message { enum class Type { click, destroy, activate, deactivate } type; }; typedef int (*Message_Handler)(Element* e, const Message& m); struct Element { struct Flags { enum { disabled = 1 << 0 }; }; UI* ui; Element* parent; Element* children, * next; Message_Handler handler; Rect bound, clip; int flags; Element(UI* ui, Element* parent); virtual ~Element(); void destroy(); virtual Rect layout(const Rect& avail); void update(); virtual void on_update(); virtual void on_render(); virtual void on_add_child(Element* nch); virtual void on_message(const Message& msg); void add_child(Element* ch); void remove_child(Element* ch); void message(const Message& msg); void render(); bool enabled(); void disable(); void enable(); }; struct Container : Element { int padding; Container(UI* ui, Element* parent); Rect layout(const Rect& avail) override; void on_render() override; }; struct Toolbar : Element { int padding; Toolbar(UI* ui, Element* parent); Rect layout(const Rect& avail) override; void on_render() override; }; struct Label : Element { char* text; Label(UI* ui, Element* parent, const char* label); ~Label(); Rect layout(const Rect& avail) override; void on_render() override; void set_text(const char* s); }; struct Button : Label { Button(UI* ui, Element* parent, const char* label); Rect layout(const Rect& avail) override; void on_render() override; }; struct Modal : Element { Container* contents; Toolbar* title_bar; Label* title; Button* close; int drag_offset[2]; int pos[2]; bool dragging; Modal(UI* ui, Element* parent, const char* title); Rect layout(const Rect& avail) override; void on_render() override; void on_update() override; void bring_to_front(); }; struct Tree : Element { Element* collapsed; /* <- children are moved here when it's collapsed so they stop layout'ing and rendering! */ Button* expandbtn; Label* text; Tree(UI* ui, Element* parent, const char* text); Rect layout(const Rect& avail) override; void on_render() override; void on_update() override; void on_add_child(Element* child) override; void expand(); void collapse(); bool is_leaf() const; }; }; #endif