Files
MontaukOS/template/sysroot/include/gui/mtk/table.hpp
T

151 lines
4.7 KiB
C++

/*
* table.hpp
* Montauk Toolkit table/list helpers
* Copyright (c) 2026 Daniel Hammer
*/
#pragma once
#include "gui/canvas.hpp"
#include "gui/mtk/theme.hpp"
namespace gui::mtk {
struct TableColumn {
const char* label;
int x;
int w;
};
struct TableLayout {
Rect header;
Rect body;
int row_h;
};
struct TableStyle {
Color header_bg;
Color header_border;
Color header_text;
Color row_selected_bg;
Color row_hover_bg;
};
inline TableStyle make_table_style(const Theme& theme) {
return {
theme.surface_alt,
theme.border,
theme.text_subtle,
theme.accent_soft,
colors::TRANSPARENT,
};
}
inline int table_visible_rows(const TableLayout& layout) {
if (layout.row_h <= 0 || layout.body.h <= 0) return 0;
return layout.body.h / layout.row_h;
}
inline int table_max_scroll(const TableLayout& layout, int row_count) {
int visible = table_visible_rows(layout);
if (row_count > visible && visible > 0) return row_count - visible;
return 0;
}
inline void clamp_table_scroll(int* scroll, const TableLayout& layout, int row_count) {
if (!scroll) return;
*scroll = gui_clamp(*scroll, 0, table_max_scroll(layout, row_count));
}
inline void ensure_table_row_visible(int* scroll,
const TableLayout& layout,
int row_count,
int selected) {
if (!scroll || selected < 0) return;
int visible = table_visible_rows(layout);
if (visible <= 0) return;
if (selected < *scroll) {
*scroll = selected;
} else if (selected >= *scroll + visible) {
*scroll = selected - visible + 1;
}
clamp_table_scroll(scroll, layout, row_count);
}
inline Rect table_row_rect(const TableLayout& layout, int scroll, int row_index) {
return {
layout.body.x,
layout.body.y + (row_index - scroll) * layout.row_h,
layout.body.w,
layout.row_h
};
}
inline Rect table_cell_rect(const Rect& row_rect, const TableColumn& col) {
return {row_rect.x + col.x, row_rect.y, col.w, row_rect.h};
}
inline int table_hit_row(const TableLayout& layout,
int scroll,
int row_count,
int mx,
int my) {
if (!layout.body.contains(mx, my) || layout.row_h <= 0) return -1;
int row = (my - layout.body.y) / layout.row_h;
int index = scroll + row;
if (index < 0 || index >= row_count) return -1;
return index;
}
using TableRowRenderer = void (*)(Canvas& c,
void* userdata,
int row_index,
const Rect& row_rect,
int text_y,
const TableColumn* cols,
int col_count,
const Theme& theme,
const TableStyle& style);
inline void draw_table(Canvas& c,
const TableLayout& layout,
const TableColumn* cols,
int col_count,
int row_count,
int scroll,
int selected,
int hovered,
const Theme& theme,
const TableStyle& style,
TableRowRenderer draw_row,
void* userdata) {
c.fill_rect(layout.header.x, layout.header.y, layout.header.w, layout.header.h, style.header_bg);
c.hline(layout.header.x, layout.header.y + layout.header.h - 1, layout.header.w, style.header_border);
int fh = system_font_height();
int header_text_y = layout.header.y + (layout.header.h - fh) / 2;
for (int i = 0; i < col_count; i++) {
c.text(layout.header.x + cols[i].x, header_text_y, cols[i].label, style.header_text);
}
int visible = table_visible_rows(layout);
for (int row = 0; row < visible; row++) {
int index = scroll + row;
if (index >= row_count) break;
Rect row_rect = table_row_rect(layout, scroll, index);
if (index == selected) {
c.fill_rect(row_rect.x, row_rect.y, row_rect.w, row_rect.h, style.row_selected_bg);
} else if (index == hovered && style.row_hover_bg.a > 0) {
c.fill_rect(row_rect.x, row_rect.y, row_rect.w, row_rect.h, style.row_hover_bg);
}
if (draw_row) {
int text_y = row_rect.y + (layout.row_h - fh) / 2;
draw_row(c, userdata, index, row_rect, text_y, cols, col_count, theme, style);
}
}
}
} // namespace gui::mtk