diff options
-rw-r--r-- | app.cpp | 18 | ||||
-rw-r--r-- | app.hpp | 16 | ||||
-rw-r--r-- | c2.cpp | 2 | ||||
-rw-r--r-- | ui.cpp | 339 | ||||
-rw-r--r-- | ui.hpp | 73 |
5 files changed, 422 insertions, 26 deletions
@@ -551,35 +551,35 @@ void App::end() { internal->end(this); } -Key_State App::ks(Key k) { +Key_State App::ks(Key k)const { return (Key_State)key_states[k]; } -Key_State App::ms(Mbtn k) { +Key_State App::ms(Mbtn k) const { return (Key_State)mbtn_states[k]; } -bool App::kp(Key k) { +bool App::kp(Key k)const { return key_states[k] & key_state_pressed; } -bool App::kjp(Key k) { +bool App::kjp(Key k) const { return key_states[k] & key_state_just_pressed; } -bool App::kjr(Key k) { +bool App::kjr(Key k) const { return key_states[k] & key_state_just_released; } -bool App::mp(Mbtn b) { +bool App::mp(Mbtn b) const { return mbtn_states[b] & key_state_pressed; } -bool App::mjp(Mbtn b) { +bool App::mjp(Mbtn b) const { return mbtn_states[b] & key_state_just_pressed; } -bool App::mjr(Mbtn b) { +bool App::mjr(Mbtn b) const { return mbtn_states[b] & key_state_just_released; } @@ -592,4 +592,4 @@ void App::get_vk_exts(const char** exts, int& count) { #else #error #endif -}
\ No newline at end of file +} @@ -126,14 +126,14 @@ struct App { void begin(); void end(); - Key_State ks(Key k); - Key_State ms(Mbtn k); - bool kp(Key k); - bool kjp(Key k); - bool kjr(Key k); - bool mp(Mbtn k); - bool mjp(Mbtn k); - bool mjr(Mbtn k); + Key_State ks(Key k) const; + Key_State ms(Mbtn k) const; + bool kp(Key k) const; + bool kjp(Key k) const; + bool kjr(Key k) const; + bool mp(Mbtn k) const; + bool mjp(Mbtn k) const; + bool mjr(Mbtn k) const; void get_vk_exts(const char** exts, int& count); @@ -138,7 +138,7 @@ extern "C" int entrypoint() { app->begin(); dev->begin_frame(); - ui->update(&frame_arena); + ui->update(&frame_arena, *app); { void* mem; @@ -9,6 +9,7 @@ extern "C" { #include <string.h> #define vertex_buffer_count (256) +#define ui_padding 5 static constexpr int font_w = 960; static unsigned font_data[] = { @@ -132,6 +133,33 @@ UI::Colour::Colour(unsigned rgb, uint8_t a): UI::Rect::Rect(int x, int y, int w, int h): x(x), y(y), w(w), h(h) {} +void UI::Rect::clip(const Rect& other) { + int n; + if ((n = other.x - x) > 0) { + w -= n; + x += n; + } + if ((n = other.y - y) > 0) { + h -= n; + y += n; + } + if ((n = x + w - (other.x + other.w)) > 0) + w -= n; + if ((n = y + h - (other.y + other.h)) > 0) + h -= n; +} + +void UI::Rect::shrink(int a) { + x += a; + y += a; + w -= a * 2; + h -= a * 2; +} + +bool UI::Rect::contains(int px, int py) { + return px >= x && py >= y && px < x + w && py < y + w; +} + void UI::Vertex_Buffer::init(Device* dev) { buf.init( dev, @@ -393,17 +421,30 @@ void UI::init( shader_info.atlas_binding = sp->descriptor_binding("atlas"); shader_info.config_binding = sp->descriptor_binding("config_buffer"); sampler = create_clamped_point(device); + root = create_element<Container>(0); } void UI::destroy() { + root->~Element(); + heap_free(heap, root); device->destroy_texture(atlas); device->destroy_sampler(sampler); mesh.destroy(this); cbuffer.destroy(device); } -void UI::update(Arena* s) { +void UI::update(Arena* s, const App& app) { (void)s; + hovered = 0; + root->update(app); + if (app.mjp(mbtn_left)) + hot = hovered; + if (app.mjr(mbtn_left) && hot && hot == hovered) { + Message msg{}; + msg.type = Message::Type::click; + hot->message(msg); + hot = 0; + } } void UI::render(Arena* s, Texture_Id target) { @@ -419,6 +460,9 @@ void UI::render(Arena* s, Texture_Id target) { cbuffer.unmap(device); cbuffer.update(ctx); + Rect avail(0, 0, t.w, t.h); + root->layout(avail); + Pipeline_Builder pb(s, device); pb.begin_rp(); pb.rp_target(device->get_backbuffer(), Clear_Mode::restore); @@ -437,11 +481,292 @@ void UI::render(Arena* s, Texture_Id target) { pipeline = &pb.build(); mesh.reset(Rect(0, 0, t.w, t.h)); - mesh.add_text(this, 10, 10, "Hello, world!", 0xffffff); - mesh.add_text(this, 10, 30, "Hello, world!", 0xffffff); - mesh.set_clip(Rect(0, 0, 35, 100)); - mesh.add_text(this, 10, 50, "Hello, world!", 0xffffff); - mesh.set_clip(Rect(0, 0, t.w, t.h)); - mesh.add_text(this, 10, 70, "Hello, world!", 0xffffff); + root->render(); mesh.draw(this, ctx, *pipeline, *render_pass); } + +void UI::draw_container(const Rect& bound) { + /* todo line function lmao */ + mesh.add_rect( + this, + bound.x, + bound.y, + bound.w, + bound.h, + 0xffffff + ); + mesh.add_rect( + this, + bound.x, + bound.y, + bound.w, + 1, + 0x000000 + ); + mesh.add_rect( + this, + bound.x + 1, + bound.y + 1, + bound.w - 1, + 1, + 0xa7a7a7 + ); + mesh.add_rect( + this, + bound.x, + bound.y, + 1, + bound.h, + 0x000000 + ); + mesh.add_rect( + this, + bound.x + 1, + bound.y + 1, + 1, + bound.h - 1, + 0xa7a7a7 + ); + mesh.add_rect( + this, + bound.x + bound.w - 1, + bound.y + 1, + 1, + bound.h - 2, + 0x000000 + ); + mesh.add_rect( + this, + bound.x + 1, + bound.y + bound.h - 1, + bound.w - 1, + 1, + 0x000000 + ); +} + +void UI::draw_containeri(const Rect& bound) { + mesh.add_rect( + this, + bound.x, + bound.y, + bound.w, + bound.h, + 0xffffff + ); + mesh.add_rect( + this, + bound.x, + bound.y, + bound.w, + 1, + 0x000000 + ); + mesh.add_rect( + this, + bound.x + 1, + bound.y + bound.h - 2, + bound.w - 2, + 1, + 0xa7a7a7 + ); + mesh.add_rect( + this, + bound.x, + bound.y, + 1, + bound.h, + 0x000000 + ); + mesh.add_rect( + this, + bound.x + bound.w - 2, + bound.y + 1, + 1, + bound.h - 2, + 0xa7a7a7 + ); + mesh.add_rect( + this, + bound.x + bound.w - 1, + bound.y + 1, + 1, + bound.h - 2, + 0x000000 + ); + mesh.add_rect( + this, + bound.x + 1, + bound.y + bound.h - 1, + bound.w - 1, + 1, + 0x000000 + ); +} + +UI::Element* UI::alloc_element(size_t size) { + UI::Element* e = (UI::Element*)heap_alloc(heap, size); + assert(e != 0); + return e; +} + +UI::Element::Element(UI* ui, Element* parent): + ui(ui), + parent(parent), + children(0), + handler(0), + child_count(0) { + if (parent) + parent->add_child(this); +} + +UI::Element::~Element() { + int i, c = child_count; + for (i = 0; i < c; i++) { + Element* child = children[i]; + child->~Element(); + heap_free(ui->heap, child); + } + if (children) + heap_free(ui->heap, children); +} + +UI::Rect UI::Element::layout(const Rect& avail) { + return avail; +} + +void UI::Element::update(const App& app) { + int i, c = child_count; + if (bound.contains(app.mx, app.my)) { + ui->hovered = this; + } + for (i = 0; i < c; i++) { + children[i]->update(app); + } +} + +void UI::Element::render() { + int i, c = child_count; + Rect old_clip = ui->mesh.clip; + on_render(); + for (i = 0; i < c; i++) { + children[i]->render(); + } + ui->mesh.clip = old_clip; +} + +void UI::Element::on_message(const Message& msg) { + (void)msg; +} + +void UI::Element::on_render() { + +} + +void UI::Element::add_child(Element* ch) { + int ncc = child_count + 1; + Element** nc = (Element**)heap_alloc(ui->heap, sizeof *nc * ncc); + if (children) { + memcpy(nc, children, child_count * sizeof *children); + heap_free(ui->heap, children); + } + children = nc; + children[child_count] = ch; + child_count = ncc; +} + +void UI::Element::message(const Message& msg) { + on_message(msg); + if (handler) + handler(this, msg); +} + +UI::Container::Container(UI* ui, Element* parent): + Element(ui, parent), padding(ui_padding) {} + +UI::Rect UI::Container::layout(const Rect& avail) { + int i, c = child_count; + Rect area = avail; + Rect used = avail; + used.w = used.h = 0; + bound = avail; + clip = avail; + for (i = 0; i < c; i++) { + Rect r = children[i]->layout(area); + area.y += r.h + padding; + area.h -= r.h; + if (r.w > used.w) used.w = r.w; + used.h += r.h; + } + return used; +} + +UI::Toolbar::Toolbar(UI* ui, Element* parent): + Element(ui, parent), padding(0) {} + +UI::Rect UI::Toolbar::layout(const Rect& avail) { + int i, c = child_count; + Rect area = avail; + Rect used = avail; + used.w = used.h = 0; + bound = avail; + clip = avail; + for (i = 0; i < c; i++) { + Rect r = children[i]->layout(area); + area.x += r.w + padding; + area.w -= r.w; + if (r.h > used.h) used.h = r.h; + used.w += r.w; + } + bound = used; + bound.w = avail.w; + clip = bound; + return used; +} + +void UI::Toolbar::on_render() { + ui->mesh.set_clip(clip); + ui->mesh.add_rect( + ui, + bound.x, + bound.y, + bound.w, + bound.h, + 0xa7a7a7 + ); +} + +UI::Button::Button(UI* ui, Element* parent, const char* label): + Element(ui, parent) { + text = dup_stringh(ui->heap, label); +} + +UI::Rect UI::Button::layout(const Rect& avail) { + Rect r = { + avail.x, + avail.y, + ui->text_width(text) + ui_padding * 2, + ui->text_height(text) + ui_padding * 2 + }; + r.clip(avail); + bound = r; + clip = r; + clip.shrink(ui_padding); + return r; +} + +void UI::Button::on_render() { + if (this == ui->hot) + ui->draw_containeri(bound); + else + ui->draw_container(bound); + ui->mesh.set_clip(clip); + ui->mesh.add_text( + ui, + bound.x + ui_padding, + bound.y + ui_padding, + text, + 0x000000 + ); +} + @@ -1,9 +1,12 @@ #ifndef ui_hpp #define ui_hpp +#include "app.hpp" #include "maths.hpp" #include "video.hpp" +#include <new> + struct Arena; struct UI { struct Vertex { @@ -24,6 +27,10 @@ struct UI { 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 { @@ -77,6 +84,8 @@ struct UI { Vertex_Buffer* next; }; + struct Element; + Heap* heap; Device* device; Texture_Id atlas; @@ -87,6 +96,7 @@ struct UI { Vertex_Buffer mesh; Pipeline* pipeline; Render_Pass* render_pass; + Element* root, * hot, * hovered; struct UI_CBuffer { m4f projection; @@ -109,8 +119,69 @@ struct UI { Shader_Id sh ); void destroy(); - void update(Arena* s); + void update(Arena* s, const App& app); void render(Arena* s, Texture_Id target); + + void draw_container(const Rect& r); + void draw_containeri(const Rect& r); + + Element* alloc_element(size_t size); + template <typename T, typename... Args> + 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 + } type; + }; + + typedef int (*Message_Handler)(Element* e, const Message& m); + + struct Element { + UI* ui; + Element* parent; + Element** children; + Message_Handler handler; + Rect bound, clip; + int child_count; + + Element(UI* ui, Element* parent); + virtual ~Element(); + + virtual Rect layout(const Rect& avail); + void update(const App& app); + virtual void on_render(); + virtual void on_message(const Message& msg); + + void add_child(Element* ch); + void message(const Message& msg); + void render(); + }; + + struct Container : Element { + int padding; + Container(UI* ui, Element* parent); + + 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 Button : Element { + char* text; + Button(UI* ui, Element* parent, const char* label); + Rect layout(const Rect& avail) override; + void on_render() override; + }; }; #endif |