Skip to content

Instantly share code, notes, and snippets.

@pervognsen
Last active September 13, 2021 19:09
Show Gist options
  • Save pervognsen/75fe0a94ac690780a58fc23a6f354f38 to your computer and use it in GitHub Desktop.
Save pervognsen/75fe0a94ac690780a58fc23a6f354f38 to your computer and use it in GitHub Desktop.
#pragma comment(lib, "SDL2")
#pragma comment(lib, "SDL2main")
#include "SDL.h"
#include <vector>
#include <unordered_map>
#include <assert.h>
#include <algorithm>
struct color {
color() : r(0), g(0), b(0), a(0xFF) {}
color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) {}
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
};
struct vec {
vec() : x(0), y(0) {}
vec(int x, int y) : x(x), y(y) {}
int x;
int y;
};
struct rect {
rect() {}
rect(vec min, vec max) : min(min), max(max) {}
vec min;
vec max;
};
struct drawcmd {
drawcmd(bool filled, rect rect, color color) : filled(filled), rect(rect), color(color) {}
bool filled;
rect rect;
color color;
};
SDL_Renderer *renderer;
std::vector<drawcmd> drawlist;
void drawlist_clear() {
drawlist.clear();
}
void drawlist_push(bool filled, rect r, color c) {
drawlist.push_back({filled, r, c});
}
void drawlist_commit() {
for (auto&& cmd : drawlist) {
SDL_SetRenderDrawColor(renderer, cmd.color.r, cmd.color.g, cmd.color.b, cmd.color.a);
SDL_Rect rect = {
cmd.rect.min.x,
cmd.rect.min.y,
cmd.rect.max.x - cmd.rect.min.x,
cmd.rect.max.y - cmd.rect.min.y
};
if (cmd.filled) {
SDL_RenderFillRect(renderer, &rect);
} else {
SDL_RenderDrawRect(renderer, &rect);
}
}
}
struct layout_box {
layout_box(uint32_t id) : id(id), last_assigned(-1), last_desired(-1) {}
uint32_t id;
vec desired;
rect assigned;
std::vector<layout_box*> children;
int last_assigned;
int last_desired;
};
std::unordered_map<uint32_t, layout_box*> layout_boxes;
std::vector<layout_box*> layout_stack;
layout_box *box;
int layout_tick;
bool layout_invalidated;
layout_box *get_layout_box(uint32_t id) {
layout_box *b = layout_boxes[id];
if (!b) {
b = layout_boxes[id] = new layout_box(id);
}
return b;
}
void layout_box_begin(uint32_t id) {
layout_box *new_box = get_layout_box(id);
if (box) {
box->children.push_back(new_box);
}
layout_stack.push_back(box);
box = new_box;
if (box->last_assigned != layout_tick) {
box->assigned = rect();
}
}
void layout_box_end() {
drawlist_push(false, box->assigned, color(0xFF, 0xFF, 0xFF, 0xFF));
box = layout_stack[layout_stack.size() - 1];
layout_stack.pop_back();
}
vec get_desired(layout_box *b) {
b->last_desired = layout_tick;
return b->desired;
}
void set_assigned(layout_box *b, rect r) {
b->last_assigned = layout_tick;
b->assigned = r;
}
void set_desired(vec v) {
if (v.x != box->desired.x || v.y != box->desired.y) {
box->desired = v;
if (box->last_desired == layout_tick) {
layout_invalidated = true;
}
}
}
void hbox_begin(uint32_t id) {
layout_box_begin(id);
int assigned_width = box->assigned.max.x - box->assigned.min.x;
int excess_width = std::max(0, assigned_width - box->desired.x);
int spacing = box->children.size() > 1 ? excess_width / ((int)box->children.size() - 1) : 0;
int x = box->assigned.min.x;
for (auto&& child : box->children) {
vec child_desired = get_desired(child);
int y = std::max(box->assigned.min.y, box->assigned.max.y - child_desired.y);
int x2 = std::min(box->assigned.max.x, x + child_desired.x);
set_assigned(child, rect(vec(x, y), vec(x2, box->assigned.max.y)));
x = x2 + spacing;
}
box->children.clear();
}
void hbox_end() {
vec desired;
for (auto&& child : box->children) {
desired.x += child->desired.x;
desired.y = std::max(desired.y, child->desired.y);
}
set_desired(desired);
layout_box_end();
}
void leaf(uint32_t id, vec size, color col) {
layout_box_begin(id);
rect r = box->assigned;
r.max.x = std::min(r.max.x, r.min.x + size.x);
r.max.y = std::min(r.max.y, r.min.y + size.y);
drawlist_push(true, r, col);
set_desired(size);
layout_box_end();
}
color color1(0xFF, 0x00, 0x00, 0xFF);
color color2(0x00, 0xFF, 0x00, 0xFF);
color color3(0x00, 0x00, 0xFF, 0xFF);
int width1 = 100, width2 = 50, width3 = 150;
int height1 = 50, height2 = 150, height3 = 100;
int show1 = 1, show2 = 1, show3 = 1;
void ui() {
layout_box *root = get_layout_box(0);
set_assigned(root, rect(vec(50, 50), vec(500, 400)));
hbox_begin(0);
if (show1) leaf(1, vec(width1, height1), color1);
if (show2) leaf(2, vec(width2, height2), color2);
if (show3) leaf(3, vec(width3, height3), color3);
hbox_end();
}
int growth = 10;
int main(int argc, char **argv) {
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *window;
SDL_CreateWindowAndRenderer(640, 480, 0, &window, &renderer);
layout_tick = 1;
for (;;) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
return 0;
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_q:
show1 = !show1;
break;
case SDLK_a:
height1 = height1 + (event.key.keysym.mod ? -growth : growth);
break;
case SDLK_z:
width1 = width1 + (event.key.keysym.mod ? -growth : growth);
break;
case SDLK_w:
show2 = !show2;
break;
case SDLK_s:
height2 = height2 + (event.key.keysym.mod ? -growth : growth);
break;
case SDLK_x:
width2 = width2 + (event.key.keysym.mod ? -growth : growth);
break;
case SDLK_e:
show3 = !show3;
break;
case SDLK_d:
height3 = height3 + (event.key.keysym.mod ? -growth : growth);
break;
case SDLK_c:
width3 = width3 + (event.key.keysym.mod ? -growth : growth);
break;
}
break;
}
}
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
SDL_RenderClear(renderer);
layout_invalidated = false;
ui();
if (layout_invalidated) {
SDL_Log("Layout invalidated: %d\n", layout_tick);
layout_tick++;
drawlist_clear();
layout_invalidated = false;
ui();
if (layout_invalidated) {
SDL_Log("DOUBLE INVALIDATION BUG: %d\n", layout_tick);
}
}
layout_tick++;
drawlist_commit();
drawlist_clear();
SDL_RenderPresent(renderer);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment