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 +	); +} + |