From a9a0d1ee84397621b6172693330b48e9474b0a91 Mon Sep 17 00:00:00 2001 From: quou Date: Tue, 31 Dec 2024 13:44:32 +1100 Subject: UI modal windows --- ui.cpp | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 218 insertions(+), 52 deletions(-) (limited to 'ui.cpp') diff --git a/ui.cpp b/ui.cpp index aeb084e..8cb9b59 100644 --- a/ui.cpp +++ b/ui.cpp @@ -157,7 +157,7 @@ void UI::Rect::shrink(int a) { } bool UI::Rect::contains(int px, int py) { - return px >= x && py >= y && px < x + w && py < y + w; + return px >= x && py >= y && px < x + w && py < y + h; } void UI::Vertex_Buffer::init(Device* dev) { @@ -375,14 +375,19 @@ void UI::Vertex_Buffer::draw( start = usage; } -UI* UI::create(Device* dev, Arena* a, Shader_Id sh) { +UI* UI::create( + Device* dev, + const App* app, + Arena* a, + Shader_Id sh +) { int hs; Texture_Id atlas = create_atlas(dev); UI* u = (UI*)arena_alloc(a, sizeof *u); Heap* h = (Heap*)arena_alloc(a, sizeof *h); hs = a->size - a->ptr - allocation_default_alignment - 1; init_heap(h, arena_alloc(a, hs), hs); - u->init(dev, h, atlas, sh); + u->init(dev, app, h, atlas, sh); return u; } @@ -399,6 +404,7 @@ int UI::text_height(const char* t) { void UI::init( Device* dev, + const App* a, Heap* h, Texture_Id at, Shader_Id sh @@ -406,8 +412,11 @@ void UI::init( Shader* sp; heap = h; device = dev; + app = a; atlas = at; shader = sh; + hot = 0; + hovered = 0; mesh.init(device); cbuffer.init( device, @@ -425,26 +434,48 @@ void UI::init( } void UI::destroy() { - root->~Element(); - heap_free(heap, root); + root->destroy(); device->destroy_texture(atlas); device->destroy_sampler(sampler); mesh.destroy(this); cbuffer.destroy(device); } -void UI::update(Arena* s, const App& app) { +void UI::layout(int w, int h) { + area_w = w; + area_h = h; + Rect avail(0, 0, w, h); + root->layout(avail); + layout_dirty = 0; +} + +void UI::update(Arena* s) { (void)s; hovered = 0; - root->update(app); - if (app.mjp(mbtn_left)) + root->update(); + if (app->mjp(mbtn_left)) { + if (hovered) { + Message msg{}; + msg.type = Message::Type::activate; + hovered->message(msg); + } hot = hovered; - if (app.mjr(mbtn_left) && hot && hot == hovered) { - Message msg{}; - msg.type = Message::Type::click; - hot->message(msg); - hot = 0; } + if (app->mjr(mbtn_left)) { + if (hot) { + Message msg{}; + msg.type = Message::Type::deactivate; + hot->message(msg); + if (hot == hovered) { + Message msg2{}; + msg2.type = Message::Type::click; + hot->message(msg2); + } + hot = 0; + } + } + if (layout_dirty) + layout(area_w, area_h); } void UI::render(Arena* s, Texture_Id target) { @@ -460,9 +491,6 @@ 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); @@ -474,7 +502,7 @@ void UI::render(Arena* s, Texture_Id target) { pb.blend( Blend_Mode::add, Blend_Factor::src_alpha, - Blend_Factor::inv_src_colour + Blend_Factor::inv_src_alpha ); pb.cbuffer(shader_info.config_binding, cbuffer.gpuonly); pb.texture(shader_info.atlas_binding, atlas, sampler); @@ -485,7 +513,7 @@ void UI::render(Arena* s, Texture_Id target) { mesh.draw(this, ctx, *pipeline, *render_pass); } -void UI::draw_container(const Rect& bound) { +void UI::draw_container(const Rect& bound, Colour c) { /* todo line function lmao */ mesh.add_rect( this, @@ -493,7 +521,7 @@ void UI::draw_container(const Rect& bound) { bound.y, bound.w, bound.h, - 0xffffff + c ); mesh.add_rect( this, @@ -545,14 +573,14 @@ void UI::draw_container(const Rect& bound) { ); } -void UI::draw_containeri(const Rect& bound) { +void UI::draw_containeri(const Rect& bound, Colour c) { mesh.add_rect( this, bound.x, bound.y, bound.w, bound.h, - 0xffffff + c ); mesh.add_rect( this, @@ -614,43 +642,58 @@ UI::Element::Element(UI* ui, Element* parent): ui(ui), parent(parent), children(0), + next(0), handler(0), - child_count(0) { + flags(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]; + Element* child; + for (child = children; child;) { + Element* n = child->next; child->~Element(); heap_free(ui->heap, child); + child = n; } - if (children) - heap_free(ui->heap, children); + if (parent) + parent->remove_child(this); +} + +void UI::Element::destroy() { + Message m{}; + m.type = Message::Type::destroy; + message(m); + this->~Element(); + heap_free(ui->heap, this); } 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)) { +void UI::Element::update() { + Element* child; + if (enabled() && bound.contains(ui->app->mx, ui->app->my)) { ui->hovered = this; } - for (i = 0; i < c; i++) { - children[i]->update(app); + on_update(); + for (child = children; child; child = child->next) { + child->update(); } } +void UI::Element::on_update() { + +} + void UI::Element::render() { - int i, c = child_count; + Element* child; Rect old_clip = ui->mesh.clip; on_render(); - for (i = 0; i < c; i++) { - children[i]->render(); + for (child = children; child; child = child->next) { + child->render(); } ui->mesh.clip = old_clip; } @@ -663,16 +706,39 @@ void UI::Element::on_render() { } +bool UI::Element::enabled() { + return (flags & Flags::disabled) == 0; +} + +void UI::Element::disable() { + flags |= Flags::disabled; +} + +void UI::Element::enable() { + flags &= ~Flags::disabled; +} + 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); + if (!children) + children = ch; + else { + Element* child = children; + for (; child && child->next; child = child->next); + child->next = ch; } - children = nc; - children[child_count] = ch; - child_count = ncc; + ui->layout_dirty = true; +} + +void UI::Element::remove_child(Element* ch) { + Element* child = children; + Element* prev = 0; + for (; child && child != ch; child = child->next) + prev = child; + assert(child != 0); + if (prev) + prev->next = ch->next; + else + children = ch->next; } void UI::Element::message(const Message& msg) { @@ -685,19 +751,20 @@ 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; + Element* child; 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); + for (child = children; child; child = child->next) { + Rect r = child->layout(area); area.y += r.h + padding; area.h -= r.h; if (r.w > used.w) used.w = r.w; used.h += r.h; } + bound = used; return used; } @@ -705,14 +772,14 @@ 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; + Element* child; 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); + for (child = children; child; child = child->next) { + Rect r = child->layout(area); area.x += r.w + padding; area.w -= r.w; if (r.h > used.h) used.h = r.h; @@ -741,6 +808,10 @@ UI::Button::Button(UI* ui, Element* parent, const char* label): text = dup_stringh(ui->heap, label); } +UI::Button::~Button() { + heap_free(ui->heap, text); +} + UI::Rect UI::Button::layout(const Rect& avail) { Rect r = { avail.x, @@ -756,10 +827,49 @@ UI::Rect UI::Button::layout(const Rect& avail) { } void UI::Button::on_render() { - if (this == ui->hot) - ui->draw_containeri(bound); + Colour tc = 0x000000; + if (flags & Flags::disabled) + tc = 0x4f4f4f; + if (this == ui->hovered) + ui->draw_container(bound, 0xf0f8ff); + else if (this == ui->hot) + ui->draw_containeri(bound, 0xffffff); else - ui->draw_container(bound); + ui->draw_container(bound, 0xffffff); + ui->mesh.set_clip(clip); + ui->mesh.add_text( + ui, + bound.x + ui_padding, + bound.y + ui_padding, + text, + tc + ); +} + +UI::Label::Label(UI* ui, Element* parent, const char* label): + Element(ui, parent) { + text = dup_stringh(ui->heap, label); +} + +UI::Label::~Label() { + heap_free(ui->heap, text); +} + +UI::Rect UI::Label::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::Label::on_render() { ui->mesh.set_clip(clip); ui->mesh.add_text( ui, @@ -770,3 +880,59 @@ void UI::Button::on_render() { ); } +static int drag_handler(UI::Modal* modal, const UI::Message& msg) { + if (msg.type == UI::Message::Type::activate) { + const App& app = *modal->ui->app; + const UI::Rect& tb = modal->title_bar->bound; + modal->dragging = true; + modal->drag_offset[0] = app.mx - tb.x; + modal->drag_offset[1] = app.my - tb.y; + } + if (msg.type == UI::Message::Type::deactivate) + modal->dragging = false; + return 0; +}; + +UI::Modal::Modal(UI* ui, Element* parent, const char* text): + Element(ui, parent), dragging(false) { + contents = ui->create_element(this); + title_bar = ui->create_element(contents); + title_bar->handler = [](Element* e, const Message& m) { + return drag_handler((Modal*)e->parent->parent, m); + }; + close = ui->create_element