diff options
Diffstat (limited to 'ui.cpp')
-rw-r--r-- | ui.cpp | 339 |
1 files changed, 332 insertions, 7 deletions
@@ -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 + ); +} + |