diff options
author | quou <quou@disroot.org> | 2024-12-31 13:44:32 +1100 |
---|---|---|
committer | quou <quou@disroot.org> | 2024-12-31 13:44:32 +1100 |
commit | a9a0d1ee84397621b6172693330b48e9474b0a91 (patch) | |
tree | dadae19cdd20d09903438ee07af69b7694af6fef | |
parent | 0c7b8be8e7d257f2f584121d97bfb899085aa350 (diff) |
UI modal windows
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | c2.cpp | 13 | ||||
-rw-r--r-- | debugger.cpp | 40 | ||||
-rw-r--r-- | debugger.hpp | 8 | ||||
-rw-r--r-- | pipeline.cpp | 6 | ||||
-rw-r--r-- | ui.cpp | 270 | ||||
-rw-r--r-- | ui.hpp | 65 |
7 files changed, 340 insertions, 67 deletions
@@ -7,7 +7,7 @@ textures = $(data_dir)/22.tex $(data_dir)/kita.tex models = $(data_dir)/monkey.mdl packed_files = $(shaders) $(textures) $(models) tools = qstd cfg sc -objects = app.o c2.o video.o pipeline.o asset.o ui.o maths.o model.o +objects = app.o c2.o video.o pipeline.o asset.o ui.o maths.o model.o debugger.o includes = -Iqstd defines = -Dplat_x86 -Dplat_posix -Dplat_x11 -Dallocation_default_alignment=8 cflags = -MMD -MF $(basename $@).d $(includes) $(defines) $(DEBUG_COMPILE_FLAG) @@ -77,6 +77,9 @@ maths.o: model.o: $(CXX) -c $(cflags) model.cpp -o model.o +debugger.o: + $(CXX) -c $(cflags) debugger.cpp -o debugger.o + c2.o: $(CXX) -c $(cflags) c2.cpp -o c2.o @@ -1,4 +1,5 @@ #include "app.hpp" +#include "debugger.hpp" #include "model.hpp" #include "ui.hpp" #include "video.hpp" @@ -22,7 +23,9 @@ static float verts[] = { struct C2 : public App { Device* dev; + UI* ui; void on_resize() override { + ui->layout(w, h); dev->on_resize(); } }; @@ -84,8 +87,8 @@ extern "C" int entrypoint() { Model* monkey; Buffer_Id vbo, cbuf; Sampler_Id clamped_linear; - UI* ui; C2* app = App::create<C2>("c2"); + UI* ui; void* per_frame; int frame = 0; app->running = 1; @@ -125,12 +128,16 @@ extern "C" int entrypoint() { per_frame_memory_size ); clamped_linear = create_clamped_linear(dev); - ui = UI::create(dev, &ui_arena, ui_shader->id); + ui = UI::create(dev, app, &ui_arena, ui_shader->id); + app->ui = ui; assert(per_frame != 0); uint8_t r = 0; float rot = 0.0f; v3f raxis(0.0f, 1.0f, 0.0); vbo = upload_verts(dev); + ui->layout(app->w, app->h); + auto toolbar = ui->create_element<UI::Toolbar>(ui->root); + register_debuggers(ui, toolbar); while (app->running) { Arena frame_arena; init_arena(&frame_arena, per_frame, per_frame_memory_size); @@ -138,7 +145,7 @@ extern "C" int entrypoint() { app->begin(); dev->begin_frame(); - ui->update(&frame_arena, *app); + ui->update(&frame_arena); { void* mem; diff --git a/debugger.cpp b/debugger.cpp new file mode 100644 index 0000000..6a71e15 --- /dev/null +++ b/debugger.cpp @@ -0,0 +1,40 @@ +#include "debugger.hpp" +extern "C" { +#include "plat.h" +} + +struct Vram_Debugger : UI::Modal { + UI::Element* enable_btn; + + Vram_Debugger(UI* ui, UI::Element* parent): + UI::Modal(ui, parent, "VRAM debugger") { + } +}; + +void create_vram_debugger( + UI* ui, + UI::Element* parent, + UI::Element* button +) { + auto window = ui->create_element<Vram_Debugger>(parent); + window->enable_btn = button; + window->handler = [](UI::Element* e, const UI::Message& m) { + if (m.type == UI::Message::Type::destroy) { + auto window = (Vram_Debugger*)e; + window->enable_btn->enable(); + } + return 0; + }; + ui->create_element<UI::Label>(window->contents, "Hello, I'm the VRAM debugger!"); +} + +void register_debuggers(UI* ui, UI::Toolbar* toolbar) { + auto btnvram = ui->create_element<UI::Button>(toolbar, "VRAM Debugger"); + btnvram->handler = [](UI::Element* e, const UI::Message& m) { + if (m.type == UI::Message::Type::click) { + e->disable(); + create_vram_debugger(e->ui, e->ui->root, e); + } + return 0; + }; +} diff --git a/debugger.hpp b/debugger.hpp new file mode 100644 index 0000000..0124f13 --- /dev/null +++ b/debugger.hpp @@ -0,0 +1,8 @@ +#ifndef debugger_hpp +#define debugger_hpp + +#include "ui.hpp" + +void register_debuggers(UI* ui, UI::Toolbar* toolbar); + +#endif diff --git a/pipeline.cpp b/pipeline.cpp index cccd1c5..1b55966 100644 --- a/pipeline.cpp +++ b/pipeline.cpp @@ -171,9 +171,9 @@ void Pipeline_Builder::blend( Blend_Factor dst ) { blend( - Blend_Mode::add, - Blend_Factor::src_colour, - Blend_Factor::inv_src_alpha, + mode, + src, + dst, mode, src, dst @@ -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<Container>(this); + title_bar = ui->create_element<Toolbar>(contents); + title_bar->handler = [](Element* e, const Message& m) { + return drag_handler((Modal*)e->parent->parent, m); + }; + close = ui->create_element<Button>(title_bar, "x"); + title = ui->create_element<Label>(title_bar, text); + title->handler = [](Element* e, const Message& m) { + return drag_handler((Modal*)e->parent->parent->parent, m); + }; + close->handler = [](Element* e, const Message& m) { + if (m.type == Message::Type::click) + e->parent->parent->parent->destroy(); + return 0; + }; +} + +UI::Rect UI::Modal::layout(const Rect& avail) { + UI::Rect content_size = contents->layout(avail); + title_bar->layout(content_size); + bound = content_size; + clip = content_size; + return content_size; +} + +void UI::Modal::on_render() { + ui->draw_container( + contents->bound, + Colour(0xffffff, 0x80) + ); +} + +void UI::Modal::on_update() { + if (dragging) { + const App& app = *ui->app; + Rect avail = bound; + avail.x = app.mx - drag_offset[0]; + avail.y = app.my - drag_offset[1]; + layout(avail); + } +} @@ -88,6 +88,7 @@ struct UI { Heap* heap; Device* device; + const App* app; Texture_Id atlas; Shader_Id shader; Vertex_Format_Id vertex_format; @@ -97,6 +98,8 @@ struct UI { Pipeline* pipeline; Render_Pass* render_pass; Element* root, * hot, * hovered; + int area_w, area_h; + bool layout_dirty; struct UI_CBuffer { m4f projection; @@ -110,20 +113,27 @@ struct UI { static int text_width(const char* t); static int text_height(const char* t); - static UI* create(Device* dev, Arena* a, Shader_Id sh); + 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 update(Arena* s, const App& app); + void layout(int w, int h); + void update(Arena* s); void render(Arena* s, Texture_Id target); - void draw_container(const Rect& r); - void draw_containeri(const Rect& r); + void draw_container(const Rect& r, Colour c); + void draw_containeri(const Rect& r, Colour c); Element* alloc_element(size_t size); template <typename T, typename... Args> @@ -135,31 +145,48 @@ struct UI { struct Message { enum class Type { - click + 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; + Element* children, * next; Message_Handler handler; Rect bound, clip; - int child_count; + int flags; Element(UI* ui, Element* parent); virtual ~Element(); + void destroy(); + virtual Rect layout(const Rect& avail); - void update(const App& app); + void update(); + virtual void on_update(); virtual void on_render(); 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 { @@ -179,8 +206,30 @@ struct UI { struct Button : Element { char* text; Button(UI* ui, Element* parent, const char* label); + ~Button(); + 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; + }; + + struct Modal : Element { + Container* contents; + Toolbar* title_bar; + Label* title; + Button* close; + int drag_offset[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; }; }; |