#ifndef ui_hpp #define ui_hpp #include "app.hpp" #include "maths.hpp" #include struct Arena; struct Heap; struct UI { 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 clip_src(const Rect& other, Rect& src); void shrink(int a); bool contains(int px, int py); }; struct Renderer { static constexpr int grid_size = 16; static constexpr int cmd_buf_size = 1024 * 128; Rect bound; Rect clip; Colour* pixels; Heap* arena; uint8_t cmd_buf[cmd_buf_size]; int cmd_buf_ptr; int grid_cell_size[2]; uint64_t grid_a[grid_size * grid_size]; uint64_t grid_b[grid_size * grid_size]; uint64_t* grid; struct Cmd { enum { RECT, TEXT, CLIP }; int type, size; Colour col; Rect r; }; void blit_rect(int x, int y, int w, int h, Colour col); void blit_char(char ch, int x, int y, Colour col); Cmd* add_cmd(int size); void commit_cmd(Cmd* c); void add_rect(int x, int y, int w, int h, Colour col); void add_text(int x, int y, const char* txt, Colour col); void reset(const Rect& cl); void set_clip(const Rect& r); void clear(const Rect& r); void flush_cell(int x, int y); bool flush(void* target); void resize(int w, int h); void init(Heap* heap, int w, int h); } ren; struct Element; Heap* heap; const App* app; Element* root, * hot, * hovered, * active; Rect area; bool layout_dirty; static int text_width(const char* t); static int text_height(const char* t); static UI* create( const App* app, Arena* a ); void init( const App* app, Heap* h ); void destroy(); void layout(int w, int h); void text_input(const char* buf); void update(Arena* s); bool render(Arena* s, void* pixels); 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, input_changed, input_finalised, text_typed, text_backspaced } type; void* payload; }; 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, bool recurse = false); 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 Table : Container { int* rows; Table(UI* ui, Element* parent, const int* rows); ~Table(); Rect layout(const Rect& avail) 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); void set_int(int v); void set_hex(uint64_t v); void set_float(float v); }; struct Button : Label { Button(UI* ui, Element* parent, const char* label); Rect layout(const Rect& avail) override; void on_render() override; }; typedef int (*Input_Filter)(char ch); struct Input : Element { Input_Filter filter; char* buf; int buf_size; Input(UI* ui, Element* parent, Input_Filter f); ~Input(); Rect layout(const Rect& avail) override; void on_render() override; void on_message(const Message& m) override; void set_text(const char* t); void add_text(const char* s); }; struct Float_Input : Input { float val; Float_Input(UI* ui, Element* parent); void on_message(const Message& m) override; void set_val(float v); }; struct Slider : Element { Float_Input* input; float minval, maxval; int drag_off; bool dragging; Slider(UI* ui, Element* parent, float miv, float mav); Rect layout(const Rect& avail) override; void on_message(const Message& m) override; void on_render() override; void on_update() override; int track_w(); Rect handle_rect(); Rect track_rect(); float val_from_coord(int coord); }; 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