summaryrefslogtreecommitdiff
path: root/kon
diff options
context:
space:
mode:
Diffstat (limited to 'kon')
-rw-r--r--kon/konui.c444
-rw-r--r--kon/konui.h48
2 files changed, 491 insertions, 1 deletions
diff --git a/kon/konui.c b/kon/konui.c
index 13a51d3..85ecd72 100644
--- a/kon/konui.c
+++ b/kon/konui.c
@@ -33,6 +33,32 @@
#define KON_UI_IDS 16
#define KON_UI_TEXTQUEUE 32
+#define KON_UI_MENUWIDTHS 8
+#define KON_UI_POPUP_MAX 600
+#define KON_UI_BAR_ID 0xB4A7C0DEu
+#define KON_UI_POPUP_ID 0x7F4A7C15u
+
+enum {
+ KON_UI_NONE,
+ KON_UI_AREA,
+ KON_UI_BAR,
+ KON_UI_POPUP
+};
+
+/* a buffer a part of the ui is drawn to before it is copied to the real framebuffer */
+typedef struct kon_uiScratch {
+ kon_framebuffer_t fb;
+ size_t cap;
+} kon_uiScratch_t;
+
+/* what a container takes over from the ui while it draws */
+typedef struct kon_uiSaved {
+ kon_framebuffer_t *fb;
+ int cx, cy, availW, sameLine;
+ int mouseX, mouseY;
+ kon_rect_t last;
+ uint32_t panelId;
+} kon_uiSaved_t;
struct kon_ui {
kon_font_t *font;
@@ -42,6 +68,7 @@ struct kon_ui {
int mouseX, mouseY;
int mouseDown, mousePressed, mouseReleased;
+ int wheel;
int keyPressed[KON_KEY_COUNT];
unsigned int text[KON_UI_TEXTQUEUE];
int textCount;
@@ -61,6 +88,26 @@ struct kon_ui {
int sameLine;
kon_rect_t last;
int cursor;
+
+ int container;
+ kon_uiSaved_t saved;
+
+ kon_uiScratch_t areaBuf;
+ kon_uiArea_t *area;
+ int areaBar, areaThumbY, areaThumbH;
+
+ kon_uiScratch_t popupBuf;
+ kon_rect_t barRect;
+ int barDrawn;
+ int menuX;
+ uint32_t menuOpen;
+ uint32_t popupMenu;
+ int popupBuilding, popupShow, popupPushed;
+ int popupX, popupY, popupH;
+ int popupNeed;
+ uint32_t menuWidthId[KON_UI_MENUWIDTHS];
+ int menuWidth[KON_UI_MENUWIDTHS];
+ int menuWidthNext;
};
/*** private helpers ***/
@@ -171,6 +218,62 @@ static uint32_t kon_uiWidgetColor_(const kon_ui_t *ui, uint32_t id, int hovered)
return hovered ? ui->theme.hover : ui->theme.widget;
}
+static int kon_uiScratchSize_(kon_uiScratch_t *s, int w, int h)
+{
+ size_t need;
+ uint32_t *data;
+
+ if (w <= 0 || h <= 0 || w > 16384 || h > 16384) return 0;
+ need = (size_t)w * (size_t)h;
+ if (need > s->cap) {
+ data = realloc(s->fb.data, need * sizeof(uint32_t));
+ if (!data) return 0;
+ s->fb.data = data;
+ s->cap = need;
+ }
+ s->fb.width = w;
+ s->fb.height = h;
+ return 1;
+}
+
+/* the buffers are opaque so what is drawn in them can be copied over the screen without any blending */
+static uint32_t kon_uiOpaque_(uint32_t color)
+{
+ return color | 0xFF000000u;
+}
+
+static void kon_uiEnter_(kon_ui_t *ui, kon_framebuffer_t *fb, int x, int y, uint32_t id)
+{
+ ui->saved.fb = ui->fb;
+ ui->saved.cx = ui->cx;
+ ui->saved.cy = ui->cy;
+ ui->saved.availW = ui->availW;
+ ui->saved.sameLine = ui->sameLine;
+ ui->saved.mouseX = ui->mouseX;
+ ui->saved.mouseY = ui->mouseY;
+ ui->saved.last = ui->last;
+ ui->saved.panelId = ui->panelId;
+
+ ui->fb = fb;
+ ui->mouseX -= x;
+ ui->mouseY -= y;
+ ui->sameLine = 0;
+ ui->panelId = id;
+}
+
+static void kon_uiLeave_(kon_ui_t *ui)
+{
+ ui->fb = ui->saved.fb;
+ ui->cx = ui->saved.cx;
+ ui->cy = ui->saved.cy;
+ ui->availW = ui->saved.availW;
+ ui->sameLine = ui->saved.sameLine;
+ ui->mouseX = ui->saved.mouseX;
+ ui->mouseY = ui->saved.mouseY;
+ ui->last = ui->saved.last;
+ ui->panelId = ui->saved.panelId;
+}
+
/*** ui ***/
kon_ui_t *kon_uiCreate(kon_font_t *font)
@@ -203,6 +306,9 @@ kon_ui_t *kon_uiCreate(kon_font_t *font)
void kon_uiDestroy(kon_ui_t *ui)
{
+ if (!ui) return;
+ free(ui->areaBuf.fb.data);
+ free(ui->popupBuf.fb.data);
free(ui);
}
@@ -234,6 +340,9 @@ void kon_uiFeedEvent(kon_ui_t *ui, const kon_event_t *event)
ui->mouseReleased = 1;
}
break;
+ case KON_EVENT_MOUSE_WHEEL:
+ ui->wheel += event->wheel;
+ break;
case KON_EVENT_KEY_DOWN:
if (event->key >= 0 && event->key < KON_KEY_COUNT) ui->keyPressed[event->key] = 1;
break;
@@ -253,6 +362,11 @@ void kon_uiBegin(kon_ui_t *ui, kon_framebuffer_t *fb)
ui->focusKept = 0;
ui->panel = NULL;
ui->panelId = 0;
+ ui->container = KON_UI_NONE;
+ ui->area = NULL;
+ ui->barDrawn = 0;
+ ui->popupBuilding = 0;
+ ui->popupShow = 0;
ui->idCount = 0;
ui->sameLine = 0;
ui->cx = ui->theme.padding;
@@ -262,6 +376,25 @@ void kon_uiBegin(kon_ui_t *ui, kon_framebuffer_t *fb)
void kon_uiEnd(kon_ui_t *ui)
{
+ if (ui->popupShow && ui->popupMenu != ui->menuOpen) ui->popupShow = 0;
+
+ if (!ui->barDrawn || ui->keyPressed[KON_KEY_ESCAPE]) ui->menuOpen = 0;
+ if (ui->menuOpen && ui->mousePressed) {
+ kon_rect_t pr = KON_RECT(ui->popupX, ui->popupY, ui->popupBuf.fb.width, ui->popupH);
+
+ if (!kon_uiInside_(ui, ui->barRect) && !(ui->popupShow && kon_uiInside_(ui, pr))) ui->menuOpen = 0;
+ }
+ if (ui->popupShow && ui->popupMenu != ui->menuOpen) ui->popupShow = 0;
+
+ /* what is open is drawn last so it ends up over everything */
+ if (ui->popupShow) {
+ kon_rect_t pr = KON_RECT(ui->popupX, ui->popupY, ui->popupBuf.fb.width, ui->popupH);
+
+ kon_drawImageRegion(ui->fb, pr.x, pr.y, &ui->popupBuf.fb, KON_RECT(0, 0, pr.w, pr.h));
+ kon_uiOutline_(ui, pr, ui->theme.border);
+ if (kon_uiInside_(ui, pr)) ui->topPanelNext = KON_UI_POPUP_ID;
+ }
+
if (ui->mousePressed && !ui->focusKept) ui->focus = 0;
if (!ui->mouseDown && ui->active) ui->active = 0;
@@ -269,6 +402,7 @@ void kon_uiEnd(kon_ui_t *ui)
ui->mousePressed = 0;
ui->mouseReleased = 0;
+ ui->wheel = 0;
memset(ui->keyPressed, 0, sizeof(ui->keyPressed));
ui->textCount = 0;
ui->fb = NULL;
@@ -314,7 +448,7 @@ int kon_uiBeginPanel(kon_ui_t *ui, const char *title, kon_uiPanel_t *panel)
kon_rect_t whole, bar, arrow;
int h, hovered;
- if (ui->panel) return 0;
+ if (ui->panel || ui->container) return 0;
if (panel->w <= 0) panel->w = 220;
h = panel->collapsed ? titleH : (panel->h > 0 ? panel->h : panel->contentH);
@@ -372,6 +506,259 @@ void kon_uiEndPanel(kon_ui_t *ui)
ui->availW = ui->fb->width - ui->theme.padding * 2;
}
+/*** areas ***/
+
+int kon_uiBeginArea(kon_ui_t *ui, const char *name, kon_uiArea_t *area)
+{
+ uint32_t id = kon_uiHash_(ui, name);
+ uint32_t barId = id ^ 0x85EBCA6Bu;
+ int pad = ui->theme.padding, barW = pad * 2;
+ int maxScroll, trackH, thumbH = 0, thumbY = 0;
+ kon_rect_t whole;
+
+ if (ui->panel || ui->container) return 0;
+ if (area->w <= 0 || area->h <= 0) return 0;
+ if (area->x >= ui->fb->width || area->y >= ui->fb->height || area->x + area->w <= 0 || area->y + area->h <= 0) return 0;
+ if (!kon_uiScratchSize_(&ui->areaBuf, area->w, area->h)) return 0;
+
+ whole = KON_RECT(area->x, area->y, area->w, area->h);
+ if (kon_uiInside_(ui, whole)) ui->topPanelNext = id;
+
+ maxScroll = area->contentH - area->h;
+ if (maxScroll < 0) maxScroll = 0;
+ trackH = area->h;
+
+ if (maxScroll && ui->wheel && ui->topPanel == id && kon_uiInside_(ui, whole)) area->scroll -= ui->wheel * ui->lineH * 3;
+ if (area->scroll > maxScroll) area->scroll = maxScroll;
+ if (area->scroll < 0) area->scroll = 0;
+
+ if (maxScroll) {
+ kon_rect_t track = KON_RECT(area->x + area->w - barW, area->y, barW, trackH);
+
+ thumbH = trackH * area->h / area->contentH;
+ if (thumbH < 16) thumbH = 16;
+ if (thumbH > trackH) thumbH = trackH;
+ thumbY = area->y + (trackH - thumbH) * area->scroll / maxScroll;
+
+ if (ui->active == 0 && ui->mousePressed && ui->topPanel == id && kon_uiInside_(ui, track)) {
+ ui->active = barId;
+ ui->dragY = ui->mouseY >= thumbY && ui->mouseY < thumbY + thumbH ? ui->mouseY - thumbY : thumbH / 2;
+ }
+ if (ui->active == barId && trackH > thumbH) {
+ area->scroll = (ui->mouseY - ui->dragY - area->y) * maxScroll / (trackH - thumbH);
+ if (area->scroll > maxScroll) area->scroll = maxScroll;
+ if (area->scroll < 0) area->scroll = 0;
+ thumbY = area->y + (trackH - thumbH) * area->scroll / maxScroll;
+ }
+ } else {
+ area->scroll = 0;
+ }
+
+ ui->areaBar = maxScroll > 0;
+ ui->areaThumbY = thumbY - area->y;
+ ui->areaThumbH = thumbH;
+
+ kon_uiEnter_(ui, &ui->areaBuf.fb, area->x, area->y, id);
+ kon_clearFramebuffer(ui->fb, kon_uiOpaque_(ui->theme.panel));
+ ui->cx = pad;
+ ui->cy = pad - area->scroll;
+ ui->availW = area->w - pad * 2 - (ui->areaBar ? barW : 0);
+ ui->container = KON_UI_AREA;
+ ui->area = area;
+ return 1;
+}
+
+void kon_uiEndArea(kon_ui_t *ui)
+{
+ kon_uiArea_t *area = ui->area;
+ int pad = ui->theme.padding, barW = pad * 2;
+ uint32_t barId;
+
+ if (ui->container != KON_UI_AREA || !area) return;
+
+ area->contentH = ui->cy + area->scroll - ui->theme.spacing + pad;
+
+ if (ui->areaBar) {
+ kon_rect_t track = KON_RECT(ui->fb->width - barW, 0, barW, ui->fb->height);
+ kon_rect_t thumb = KON_RECT(track.x + 1, ui->areaThumbY, barW - 2, ui->areaThumbH);
+
+ barId = ui->panelId ^ 0x85EBCA6Bu;
+ kon_uiFill_(ui, track, ui->theme.title);
+ kon_uiFill_(ui, thumb, ui->active == barId ? ui->theme.accent : (kon_uiInside_(ui, thumb) && ui->topPanel == ui->panelId ? ui->theme.active : ui->theme.hover));
+ }
+
+ kon_uiLeave_(ui);
+ ui->container = KON_UI_NONE;
+ ui->area = NULL;
+
+ kon_drawImageRegion(ui->fb, area->x, area->y, &ui->areaBuf.fb, KON_RECT(0, 0, ui->areaBuf.fb.width, ui->areaBuf.fb.height));
+ kon_uiOutline_(ui, KON_RECT(area->x, area->y, ui->areaBuf.fb.width, ui->areaBuf.fb.height), ui->theme.border);
+}
+
+/*** menus ***/
+
+int kon_uiMenuBarHeight(const kon_ui_t *ui)
+{
+ return ui->lineH + ui->theme.padding;
+}
+
+int kon_uiBeginMenuBar(kon_ui_t *ui)
+{
+ int h = kon_uiMenuBarHeight(ui);
+ kon_rect_t r = KON_RECT(0, 0, ui->fb->width, h);
+
+ if (ui->panel || ui->container) return 0;
+ if (kon_uiInside_(ui, r)) ui->topPanelNext = KON_UI_BAR_ID;
+
+ ui->barRect = r;
+ ui->barDrawn = 1;
+ ui->menuX = 0;
+ ui->panelId = KON_UI_BAR_ID;
+ ui->container = KON_UI_BAR;
+
+ kon_uiFill_(ui, r, ui->theme.title);
+ kon_fillRectangle(ui->fb, 0, h - 1, r.w, 1, ui->theme.border);
+ return 1;
+}
+
+void kon_uiEndMenuBar(kon_ui_t *ui)
+{
+ int below;
+
+ if (ui->container != KON_UI_BAR) return;
+ ui->container = KON_UI_NONE;
+ ui->panelId = 0;
+
+ below = kon_uiMenuBarHeight(ui) + ui->theme.padding;
+ if (ui->cy < below) ui->cy = below;
+}
+
+static int kon_uiMenuWidth_(const kon_ui_t *ui, uint32_t id)
+{
+ int i;
+
+ for (i = 0; i < KON_UI_MENUWIDTHS; i++) {
+ if (ui->menuWidthId[i] == id) return ui->menuWidth[i];
+ }
+ return 160;
+}
+
+static void kon_uiSetMenuWidth_(kon_ui_t *ui, uint32_t id, int w)
+{
+ int i;
+
+ for (i = 0; i < KON_UI_MENUWIDTHS; i++) {
+ if (ui->menuWidthId[i] == id) {
+ ui->menuWidth[i] = w;
+ return;
+ }
+ }
+ i = ui->menuWidthNext;
+ ui->menuWidthNext = (i + 1) % KON_UI_MENUWIDTHS;
+ ui->menuWidthId[i] = id;
+ ui->menuWidth[i] = w;
+}
+
+int kon_uiBeginMenu(kon_ui_t *ui, const char *label)
+{
+ uint32_t id = kon_uiHash_(ui, label);
+ size_t n = kon_uiVisibleLength_(label);
+ int pad = ui->theme.padding, barH = kon_uiMenuBarHeight(ui);
+ int hovered, clicked, w, h, x;
+ kon_rect_t r;
+
+ if (ui->container != KON_UI_BAR) return 0;
+
+ r = KON_RECT(ui->menuX, 0, kon_uiTextWidth_(ui, label, n) + pad * 3, barH - 1);
+ ui->menuX += r.w;
+
+ clicked = kon_uiBehave_(ui, id, r, &hovered);
+ if (clicked) ui->menuOpen = ui->menuOpen == id ? 0 : id;
+ else if (hovered && ui->menuOpen && ui->menuOpen != id) ui->menuOpen = id;
+
+ if (ui->menuOpen == id) kon_uiFill_(ui, r, ui->theme.active);
+ else if (hovered) kon_uiFill_(ui, r, ui->theme.hover);
+ kon_uiDrawText_(ui, r.x + pad * 3 / 2, r.y + pad / 2, label, n, ui->theme.text);
+
+ if (ui->menuOpen != id) return 0;
+
+ w = kon_uiMenuWidth_(ui, id);
+ if (w < 80) w = 80;
+ if (w > 480) w = 480;
+ h = ui->fb->height - barH;
+ if (h > KON_UI_POPUP_MAX) h = KON_UI_POPUP_MAX;
+ if (h < 1 || !kon_uiScratchSize_(&ui->popupBuf, w, h)) return 0;
+
+ x = r.x;
+ if (x + w > ui->fb->width) x = ui->fb->width - w;
+ if (x < 0) x = 0;
+
+ kon_uiEnter_(ui, &ui->popupBuf.fb, x, barH, KON_UI_POPUP_ID);
+ kon_clearFramebuffer(ui->fb, kon_uiOpaque_(ui->theme.panel));
+ ui->cx = pad;
+ ui->cy = pad;
+ ui->availW = w - pad * 2;
+
+ ui->popupX = x;
+ ui->popupY = barH;
+ ui->popupMenu = id;
+ ui->popupNeed = 0;
+ ui->popupBuilding = 1;
+ ui->popupPushed = ui->idCount < KON_UI_IDS;
+ if (ui->popupPushed) kon_uiPushId(ui, (int)id);
+ ui->container = KON_UI_POPUP;
+ return 1;
+}
+
+void kon_uiEndMenu(kon_ui_t *ui)
+{
+ int h;
+
+ if (!ui->popupBuilding) return;
+
+ h = ui->cy - ui->theme.spacing + ui->theme.padding;
+ if (h > ui->popupBuf.fb.height) h = ui->popupBuf.fb.height;
+ if (h < 1) h = 1;
+ ui->popupH = h;
+ ui->popupShow = 1;
+ kon_uiSetMenuWidth_(ui, ui->popupMenu, ui->popupNeed);
+
+ if (ui->popupPushed) kon_uiPopId(ui);
+ kon_uiLeave_(ui);
+ ui->popupBuilding = 0;
+ ui->container = KON_UI_BAR;
+}
+
+int kon_uiMenuItem(kon_ui_t *ui, const char *label, const char *shortcut)
+{
+ uint32_t id = kon_uiHash_(ui, label);
+ size_t n = kon_uiVisibleLength_(label);
+ int pad = ui->theme.padding, textW = kon_uiTextWidth_(ui, label, n), shortW = 0, need, hovered, clicked;
+ kon_rect_t r;
+
+ if (!ui->popupBuilding) return 0;
+
+ r = kon_uiNextRect_(ui, 0, ui->lineH);
+ if (shortcut) shortW = kon_uiTextWidth_(ui, shortcut, strlen(shortcut));
+
+ need = pad * 4 + textW + (shortW ? pad * 4 + shortW : 0);
+ if (need > ui->popupNeed) ui->popupNeed = need;
+
+ clicked = kon_uiBehave_(ui, id, r, &hovered);
+ if (hovered) kon_uiFill_(ui, r, ui->theme.hover);
+ kon_uiDrawText_(ui, r.x + pad, r.y, label, n, ui->theme.text);
+ if (shortcut) kon_uiDrawText_(ui, r.x + r.w - shortW - pad, r.y, shortcut, strlen(shortcut), ui->theme.textDim);
+
+ if (clicked) ui->menuOpen = 0;
+ return clicked;
+}
+
+void kon_uiMenuSeparator(kon_ui_t *ui)
+{
+ if (!ui->popupBuilding) return;
+ kon_uiSeparator(ui);
+}
+
/*** widgets ***/
void kon_uiLabel(kon_ui_t *ui, const char *text)
@@ -550,6 +937,61 @@ int kon_uiTextInput(kon_ui_t *ui, const char *label, char *buffer, size_t size)
return entered;
}
+int kon_uiTabs(kon_ui_t *ui, const char *const *labels, int count, int *current)
+{
+ int pad = ui->theme.padding, changed = 0, i;
+
+ for (i = 0; i < count; i++) {
+ size_t n = kon_uiVisibleLength_(labels[i]);
+ int pushed = ui->idCount < KON_UI_IDS, hovered, clicked, selected;
+ uint32_t id, color;
+ kon_rect_t r;
+
+ if (pushed) kon_uiPushId(ui, i);
+ id = kon_uiHash_(ui, labels[i]);
+
+ if (i > 0) ui->sameLine = 1;
+ r = kon_uiNextRect_(ui, kon_uiTextWidth_(ui, labels[i], n) + pad * 3, ui->lineH + pad);
+
+ clicked = kon_uiBehave_(ui, id, r, &hovered);
+ if (clicked && *current != i) {
+ *current = i;
+ changed = 1;
+ }
+ selected = *current == i;
+
+ color = selected ? ui->theme.active : kon_uiWidgetColor_(ui, id, hovered);
+ kon_uiFill_(ui, r, color);
+ if (selected) kon_uiFill_(ui, KON_RECT(r.x, r.y, r.w, 2), ui->theme.accent);
+ kon_uiOutline_(ui, r, ui->theme.border);
+ kon_uiDrawText_(ui, r.x + pad * 3 / 2, r.y + pad / 2 + 1, labels[i], n, selected ? ui->theme.text : ui->theme.textDim);
+
+ if (pushed) kon_uiPopId(ui);
+ }
+ return changed;
+}
+
+int kon_uiTreeNode(kon_ui_t *ui, const char *label, int *open)
+{
+ uint32_t id = kon_uiHash_(ui, label);
+ size_t n = kon_uiVisibleLength_(label);
+ kon_rect_t r = kon_uiNextRect_(ui, 0, ui->lineH);
+ int hovered, clicked = kon_uiBehave_(ui, id, r, &hovered);
+
+ if (clicked) *open = !*open;
+ if (hovered) kon_uiFill_(ui, r, ui->theme.hover);
+ kon_uiDrawText_(ui, r.x + ui->theme.padding, r.y, *open ? "-" : "+", 1, ui->theme.textDim);
+ kon_uiDrawText_(ui, r.x + ui->lineH, r.y, label, n, ui->theme.text);
+ return *open;
+}
+
+void kon_uiIndent(kon_ui_t *ui, int amount)
+{
+ ui->cx += amount;
+ ui->availW -= amount;
+ if (ui->availW < 1) ui->availW = 1;
+}
+
void kon_uiSeparator(kon_ui_t *ui)
{
kon_rect_t r = kon_uiNextRect_(ui, 0, 1);
diff --git a/kon/konui.h b/kon/konui.h
index 9b253ca..98a1f08 100644
--- a/kon/konui.h
+++ b/kon/konui.h
@@ -71,6 +71,46 @@ int kon_uiWantsKeyboard(const kon_ui_t *ui);
int kon_uiBeginPanel(kon_ui_t *ui, const char *title, kon_uiPanel_t *panel);
void kon_uiEndPanel(kon_ui_t *ui);
+/*** areas ***/
+
+/* a fixed part of the window with no title and no dragging, for a layout that stays put: a side bar, a list, a preview. what is inside
+ is clipped to it and scrolls with the wheel when it is taller than the area. you own this and fill in x, y, w and h, the ui keeps
+ scroll and contentH. contentH is what the last frame needed, so the scroll bar shows up one frame late */
+typedef struct kon_uiArea {
+ int x, y, w, h;
+ int scroll;
+ int contentH;
+} kon_uiArea_t;
+
+/* returns 0 when it is not drawn (nothing of it is on screen, or it is inside a panel, an area or a menu). always call kon_uiEndArea */
+int kon_uiBeginArea(kon_ui_t *ui, const char *id, kon_uiArea_t *area);
+void kon_uiEndArea(kon_ui_t *ui);
+
+/*** menus ***/
+
+/* a bar along the top of the window, kon_uiMenuBarHeight is how tall it is so you can lay the rest out under it.
+ kon_uiBeginMenu gives 1 while that menu is open, and then its items are kon_uiMenuItem, which gives 1 when one is clicked.
+ always call kon_uiEndMenu and kon_uiEndMenuBar. what is open is drawn over everything at the end of the frame.
+ a menu is as long as its items, up to 600 pixels, the rest is cut off
+
+ if (kon_uiBeginMenuBar(ui)) {
+ if (kon_uiBeginMenu(ui, "File")) {
+ if (kon_uiMenuItem(ui, "Open", "Ctrl+O")) open();
+ kon_uiMenuSeparator(ui);
+ if (kon_uiMenuItem(ui, "Quit", NULL)) quit();
+ }
+ kon_uiEndMenu(ui);
+ }
+ kon_uiEndMenuBar(ui);
+*/
+int kon_uiBeginMenuBar(kon_ui_t *ui);
+void kon_uiEndMenuBar(kon_ui_t *ui);
+int kon_uiMenuBarHeight(const kon_ui_t *ui);
+int kon_uiBeginMenu(kon_ui_t *ui, const char *label);
+int kon_uiMenuItem(kon_ui_t *ui, const char *label, const char *shortcut);
+void kon_uiMenuSeparator(kon_ui_t *ui);
+void kon_uiEndMenu(kon_ui_t *ui);
+
/* anything after ## in a label is part of the id but not drawn, so "ok##a" and "ok##b" are different buttons */
void kon_uiLabel(kon_ui_t *ui, const char *text);
void kon_uiLabelf(kon_ui_t *ui, const char *format, ...);
@@ -83,6 +123,14 @@ int kon_uiSelectable(kon_ui_t *ui, const char *label, int selected);
/* returns 1 when enter is pressed. label is shown while the field is empty */
int kon_uiTextInput(kon_ui_t *ui, const char *label, char *buffer, size_t size);
+/* a row of tabs, current is the one shown. returns 1 when another one was clicked */
+int kon_uiTabs(kon_ui_t *ui, const char *const *labels, int count, int *current);
+
+/* a line with a + or - in front that opens and closes. returns the new open state, show what is inside when it is 1 and use
+ kon_uiIndent to push it in */
+int kon_uiTreeNode(kon_ui_t *ui, const char *label, int *open);
+void kon_uiIndent(kon_ui_t *ui, int amount);
+
void kon_uiSeparator(kon_ui_t *ui);
void kon_uiSameLine(kon_ui_t *ui);
void kon_uiPushId(kon_ui_t *ui, int id);