tui lib has table drawing fn

This commit is contained in:
Tenari
2026-02-19 07:36:53 -06:00
parent d208d14520
commit 10e751e115
4 changed files with 154 additions and 122 deletions
+29
View File
@@ -189,6 +189,10 @@
# define assertBreak() (*(volatile int*)0 = 0)
#endif
#ifndef offsetof
#define offsetof(st, m) ((size_t)&(((st*)0)->m))
#endif
#if ENABLE_ASSERT
# define assert(c) stmnt( if (!(c)){ assertBreak(); } )
#else
@@ -319,6 +323,24 @@ typedef enum Utf8Character {
Utf8Character_Count,
} Utf8Character;
typedef enum FieldType {
FieldTypeU8,
FieldTypeU16,
FieldTypeU32,
FieldTypeFloat,
FieldTypeString,
FieldTypeEnum,
FieldType_Count,
} FieldType;
typedef struct FieldDescriptor {
str name;
FieldType type;
size_t offset;
int width; // column width for display
str* enum_vals;
} FieldDescriptor;
typedef struct Box {
u32 x;
u32 y;
@@ -326,6 +348,13 @@ typedef struct Box {
u32 width;
} Box;
typedef struct TableDrawInfo {
u32 x_offset;
u32 y_offset;
u32 rows;
u32 cols;
} TableDrawInfo;
typedef struct Dim2 {
u16 height;
u16 width;
+19 -98
View File
@@ -583,63 +583,11 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
renderStrToBuffer(tui->frame_buffer, 5, ++line, tmp_buffer, screen_dimensions);
line++;
// the table
u32 table_x = 5;
u32 table_col_pad = 1;
// render the headers
u32 col_x_pos = 0;
for (u32 i = 0; i < SHIP_DETAIL_COUNT; col_x_pos += SHIP_FIELDS[i].width+table_col_pad, i++) {
renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, SHIP_FIELDS[i].name, screen_dimensions);
for (u32 ii = 0; ii < SHIP_FIELDS[i].width; ii++) {
renderUtf8CharToBuffer(tui->frame_buffer, table_x+col_x_pos+ii, line+1, "", screen_dimensions);
}
}
line++;
line++;
// render the rows
for (u32 i = 0; i < ShipType_Count; line++, i++) {
col_x_pos = 0;
ShipTemplate ship = SHIPS[i];
// render the columns
for (u32 ii = 0; ii < SHIP_DETAIL_COUNT; col_x_pos += SHIP_FIELDS[ii].width+table_col_pad, ii++) {
if (i == state->menu.selected_index) {
for (u32 iii = 0; iii < SHIP_FIELDS[ii].width+table_col_pad; iii++) {
u32 pos = XYToPos(table_x+col_x_pos+iii, line, screen_dimensions.width);
tui->frame_buffer[pos].background = ANSI_WHITE;
tui->frame_buffer[pos].foreground = ANSI_BLACK;
tui->frame_buffer[pos].bytes[0] = ' ';
}
}
FieldDescriptor details = SHIP_FIELDS[ii];
void *field_ptr = (char *)&ship + details.offset;
MemoryZero(tmp_buffer, 64);
switch (details.type) {
case FieldTypeEnum: {
renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, details.enum_vals[ship.type], screen_dimensions);
} break;
case FieldTypeU8: {
sprintf(tmp_buffer, "%d", *(u8 *)field_ptr);
renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, tmp_buffer, screen_dimensions);
} break;
case FieldTypeU16: {
sprintf(tmp_buffer, "%d", *(u16 *)field_ptr);
renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, tmp_buffer, screen_dimensions);
} break;
case FieldTypeU32: {
sprintf(tmp_buffer, "%d", *(u32 *)field_ptr);
renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, tmp_buffer, screen_dimensions);
} break;
case FieldTypeFloat: {
sprintf(tmp_buffer, "%f", *(f32 *)field_ptr);
renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, tmp_buffer, screen_dimensions);
} break;
case FieldTypeString: {
renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, (ptr)field_ptr, screen_dimensions);
} break;
case FieldType_Count:
break;
}
}
}
TableDrawInfo info = {
.x_offset = 5, .y_offset = line,
.rows = ShipType_Count, .cols = SHIP_DETAIL_COUNT,
};
renderTable(tui, info, state->menu.selected_index, SHIP_FIELDS, 1, SHIPS, sizeof(SHIPS[0]));
} break;
case ScreenMainGame: {
if (user_pressed_tab) {
@@ -774,50 +722,23 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
renderStrToBuffer(tui->frame_buffer, box.x+2, box.y+1, "Welcome to ", screen_dimensions);
renderStrToBuffer(tui->frame_buffer, box.x+2+11, box.y+1, state->current.name, screen_dimensions);
char tmp_buffer[64] = {0};
u32 table_x = box.x+2;
u32 line = box.y+3;
str cols[6] = {"Commodity", "Unit", "#", "Bid", "Ask", "Owned"};
u32 lens[6] = { 44, 6, 6, 6, 6, 6};
u32 col_x_pos = 0;
// render headers
for (u32 i = 0; i < 6; col_x_pos += lens[i++]) {
renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, cols[i], screen_dimensions);
for (u32 ii = 0; ii < lens[i]; ii++) {
renderUtf8CharToBuffer(tui->frame_buffer, table_x+col_x_pos+ii, line+1, "", screen_dimensions);
}
}
line += 2;
// render rows
// the table
TableDrawInfo info = {
.x_offset = box.x+2, .y_offset = box.y+3,
.rows = Commodity_Count, .cols = 6,
};
MarketCommodity rows[Commodity_Count] = { 0 };
for (u32 i = 0; i < Commodity_Count; i++) {
col_x_pos = 0;
Commodity c = COMMODITIES[i];
u32 qty = state->current.planets[0].commodities[i] + state->current.planets[1].commodities[i] + state->current.planets[2].commodities[i];
for (u32 ii = 0; ii < 6; col_x_pos += lens[ii++]) {
if (i == state->row.selected_index) {
for (u32 iii = 0; iii < lens[ii]; iii++) {
u32 pos = XYToPos(table_x+col_x_pos+iii, line+i, screen_dimensions.width);
tui->frame_buffer[pos].background = ANSI_WHITE;
tui->frame_buffer[pos].foreground = ANSI_BLACK;
tui->frame_buffer[pos].bytes[0] = ' ';
}
}
MemoryZero(tmp_buffer, 64);
if (ii == 0) {
sprintf(tmp_buffer, "%s", COMMODITIES[i].name);
} else if (ii == 1) {
sprintf(tmp_buffer, "%s", COMMODITIES[i].unit == StorageUnitKg ? "kg" : "cont");
} else if (ii == 2) {
sprintf(tmp_buffer, "%d", qty);
} else if (ii == 3) {
sprintf(tmp_buffer, "%d", (u32)priceForCommodity(i, qty, true));
} else if (ii == 4) {
sprintf(tmp_buffer, "%d", (u32)priceForCommodity(i, qty, false));
} else if (ii == 5) {
sprintf(tmp_buffer, "%d", state->me.commodities[i]);
}
renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line+i, tmp_buffer, screen_dimensions);
}
rows[i].type = c.type;
rows[i].unit = c.unit;
rows[i].bid = priceForCommodity(i, qty, true);
rows[i].ask = priceForCommodity(i, qty, false);
rows[i].qty = qty;
rows[i].owned = state->me.commodities[i];
}
renderTable(tui, info, state->row.selected_index, MARKET_COMMODITY_FIELDS, 1, rows, sizeof(MarketCommodity));
} break;
case Tab_Count: {} break;
}
+58
View File
@@ -525,6 +525,64 @@ fn Pos2 renderCommandPalette(TuiState* tui, String current_search, CommandPalett
return result;
}
fn void renderTable(TuiState* tui, TableDrawInfo t, u32 selected_index, FieldDescriptor fields[], u32 table_col_pad, void* data, u32 sizeof_data_item) {
char tmp_buffer[64] = {0};
u32 col_x_pos = 0;
// render headers
for (u32 i = 0; i < t.cols; col_x_pos += fields[i++].width+table_col_pad) {
renderStrToBuffer(tui->frame_buffer, t.x_offset+col_x_pos, t.y_offset, fields[i].name, tui->screen_dimensions);
for (u32 ii = 0; ii < fields[i].width; ii++) {
renderUtf8CharToBuffer(tui->frame_buffer, t.x_offset+col_x_pos+ii, t.y_offset+1, "", tui->screen_dimensions);
}
}
t.y_offset += 2;
// render the rows
for (u32 i = 0; i < t.rows; t.y_offset++, i++) {
col_x_pos = 0;
ptr item_offset = data + (sizeof_data_item * i);
// render the columns
for (u32 ii = 0; ii < t.cols; col_x_pos += fields[ii].width+table_col_pad, ii++) {
if (i == selected_index) {
for (u32 iii = 0; iii < fields[ii].width+table_col_pad; iii++) {
u32 pos = XYToPos(t.x_offset+col_x_pos+iii, t.y_offset, tui->screen_dimensions.width);
tui->frame_buffer[pos].background = ANSI_WHITE;
tui->frame_buffer[pos].foreground = ANSI_BLACK;
tui->frame_buffer[pos].bytes[0] = ' ';
}
}
FieldDescriptor details = fields[ii];
void *field_ptr = item_offset + details.offset;
MemoryZero(tmp_buffer, 64);
switch (details.type) {
case FieldTypeEnum: {
renderStrToBuffer(tui->frame_buffer, t.x_offset+col_x_pos, t.y_offset, details.enum_vals[*(u8 *)field_ptr], tui->screen_dimensions);
} break;
case FieldTypeU8: {
sprintf(tmp_buffer, "%d", *(u8 *)field_ptr);
renderStrToBuffer(tui->frame_buffer, t.x_offset+col_x_pos, t.y_offset, tmp_buffer, tui->screen_dimensions);
} break;
case FieldTypeU16: {
sprintf(tmp_buffer, "%d", *(u16 *)field_ptr);
renderStrToBuffer(tui->frame_buffer, t.x_offset+col_x_pos, t.y_offset, tmp_buffer, tui->screen_dimensions);
} break;
case FieldTypeU32: {
sprintf(tmp_buffer, "%d", *(u32 *)field_ptr);
renderStrToBuffer(tui->frame_buffer, t.x_offset+col_x_pos, t.y_offset, tmp_buffer, tui->screen_dimensions);
} break;
case FieldTypeFloat: {
sprintf(tmp_buffer, "%f", *(f32 *)field_ptr);
renderStrToBuffer(tui->frame_buffer, t.x_offset+col_x_pos, t.y_offset, tmp_buffer, tui->screen_dimensions);
} break;
case FieldTypeString: {
renderStrToBuffer(tui->frame_buffer, t.x_offset+col_x_pos, t.y_offset, (ptr)field_ptr, tui->screen_dimensions);
} break;
case FieldType_Count:
break;
}
}
}
}
fn void renderChoiceMenu(TuiState* tui, u16 x, u16 y, ptr options[], u32 len, bool choosable, u32 selected_index, u8* colors) {
for (u32 i = 0; i < len; i++) {
u32 pos = x + (tui->screen_dimensions.width*(y+i));
+48 -24
View File
@@ -88,30 +88,6 @@ fn f32 calcInterestRate(u32 cost, u32 down_payment) {
return mortgage_rate;
}
typedef enum {
FieldTypeU8,
FieldTypeU16,
FieldTypeU32,
FieldTypeFloat,
FieldTypeString,
FieldTypeEnum,
FieldType_Count,
} FieldType;
typedef struct FieldDescriptor {
str name;
FieldType type;
size_t offset;
int width; // column width for display
str* enum_vals;
} FieldDescriptor;
// Define field metadata using offsetof
#ifndef offsetof
#define offsetof(st, m) ((size_t)&(((st*)0)->m))
#endif
// Field descriptors for Employee
global FieldDescriptor SHIP_FIELDS[SHIP_DETAIL_COUNT] = {
{ "Type", FieldTypeEnum, offsetof(ShipTemplate, type), 16, (str*)&SHIP_TYPE_STRINGS },
{ "Cost", FieldTypeU32, offsetof(ShipTemplate, base_cost), 8 },
@@ -155,6 +131,30 @@ typedef enum CommodityType {
Commodity_Count,
} CommodityType;
global str COMMODITY_STRINGS[Commodity_Count] = {
"Hydrogen Fuel",
"Oxygen",
"Water",
"Fertilizer",
"Raw Textiles",
"Low Grade Ore",
"High Grade Ore",
"Plastics",
"Grain",
"Meat",
"Spices",
"Electronics",
"Glass",
"Hand Tools",
"Semi-conductors (Silicon, Arsenic, Boron)",
"Common Metals (Iron, Nickel, Zinc)",
"Rare Metals (Titanium, Chromium)",
"Precious Metals (Silver, Gold, Platinum)",
"Alcohol",
"Clothes",
"Personal Sundries (Cutlery, Toys, Misc)",
};
typedef enum StorageUnit {
StorageUnitKg,
StorageUnitContainer,
@@ -162,6 +162,12 @@ typedef enum StorageUnit {
StorageUnit_Count,
} StorageUnit;
global str STORAGE_UNIT_STRINGS[StorageUnit_Count] = {
"kg",
"cont",
"atmo"
};
typedef struct Commodity {
CommodityType type;
StorageUnit unit;
@@ -257,6 +263,24 @@ global Commodity COMMODITIES[Commodity_Count] = {
.price = 1800, .qty = 40, .consumption = 3,
},
};
typedef struct MarketCommodity {
CommodityType type;
StorageUnit unit;
u32 bid;
u32 ask;
u32 qty;
u32 owned;
} MarketCommodity;
global FieldDescriptor MARKET_COMMODITY_FIELDS[Commodity_Count] = {
{ "Commodity", FieldTypeEnum, offsetof(MarketCommodity, type), 44, (str*)&COMMODITY_STRINGS },
{ "Unit", FieldTypeEnum, offsetof(MarketCommodity, unit), 6, (str*)&STORAGE_UNIT_STRINGS },
{ "#", FieldTypeU32, offsetof(MarketCommodity, qty), 6 },
{ "Bid", FieldTypeU32, offsetof(MarketCommodity, bid), 6 },
{ "Ask", FieldTypeU32, offsetof(MarketCommodity, ask), 6 },
{ "Owned", FieldTypeU32, offsetof(MarketCommodity, owned), 6 },
};
fn f32 priceForCommodity(CommodityType type, u32 quantity, bool bid) {
f32 result = 0.0;
Commodity details = COMMODITIES[type];