Last active
September 13, 2021 19:09
-
-
Save pervognsen/75fe0a94ac690780a58fc23a6f354f38 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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