summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app.cpp18
-rw-r--r--app.hpp16
-rw-r--r--c2.cpp2
-rw-r--r--ui.cpp339
-rw-r--r--ui.hpp73
5 files changed, 422 insertions, 26 deletions
diff --git a/app.cpp b/app.cpp
index 54fab7e..95f730b 100644
--- a/app.cpp
+++ b/app.cpp
@@ -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
+}
diff --git a/app.hpp b/app.hpp
index 95bdec7..3e2ef23 100644
--- a/app.hpp
+++ b/app.hpp
@@ -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);
diff --git a/c2.cpp b/c2.cpp
index 2e71fc8..e3e8542 100644
--- a/c2.cpp
+++ b/c2.cpp
@@ -138,7 +138,7 @@ extern "C" int entrypoint() {
app->begin();
dev->begin_frame();
- ui->update(&frame_arena);
+ ui->update(&frame_arena, *app);
{
void* mem;
diff --git a/ui.cpp b/ui.cpp
index 5814204..aeb084e 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -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
+ );
+}
+
diff --git a/ui.hpp b/ui.hpp
index 2b671ae..432b615 100644
--- a/ui.hpp
+++ b/ui.hpp
@@ -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