summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorquou <quou@disroot.org>2024-12-31 13:44:32 +1100
committerquou <quou@disroot.org>2024-12-31 13:44:32 +1100
commita9a0d1ee84397621b6172693330b48e9474b0a91 (patch)
treedadae19cdd20d09903438ee07af69b7694af6fef
parent0c7b8be8e7d257f2f584121d97bfb899085aa350 (diff)
UI modal windows
-rw-r--r--Makefile5
-rw-r--r--c2.cpp13
-rw-r--r--debugger.cpp40
-rw-r--r--debugger.hpp8
-rw-r--r--pipeline.cpp6
-rw-r--r--ui.cpp270
-rw-r--r--ui.hpp65
7 files changed, 340 insertions, 67 deletions
diff --git a/Makefile b/Makefile
index 0828805..3fc811e 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/c2.cpp b/c2.cpp
index e3e8542..d356ada 100644
--- a/c2.cpp
+++ b/c2.cpp
@@ -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
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<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);
+ }
+}
diff --git a/ui.hpp b/ui.hpp
index 432b615..5c67434 100644
--- a/ui.hpp
+++ b/ui.hpp
@@ -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;
};
};