DVX_GUI/dvx/widgets/widgetTabControl.c

253 lines
7.3 KiB
C

// widgetTabControl.c — TabControl and TabPage widgets
#include "widgetInternal.h"
// ============================================================
// wgtTabControl
// ============================================================
WidgetT *wgtTabControl(WidgetT *parent) {
WidgetT *w = widgetAlloc(parent, WidgetTabControlE);
if (w) {
w->as.tabControl.activeTab = 0;
w->weight = 100;
}
return w;
}
// ============================================================
// wgtTabControlGetActive
// ============================================================
int32_t wgtTabControlGetActive(const WidgetT *w) {
if (!w || w->type != WidgetTabControlE) {
return 0;
}
return w->as.tabControl.activeTab;
}
// ============================================================
// wgtTabControlSetActive
// ============================================================
void wgtTabControlSetActive(WidgetT *w, int32_t idx) {
if (!w || w->type != WidgetTabControlE) {
return;
}
w->as.tabControl.activeTab = idx;
}
// ============================================================
// wgtTabPage
// ============================================================
WidgetT *wgtTabPage(WidgetT *parent, const char *title) {
WidgetT *w = widgetAlloc(parent, WidgetTabPageE);
if (w) {
w->as.tabPage.title = title;
}
return w;
}
// ============================================================
// widgetTabControlCalcMinSize
// ============================================================
void widgetTabControlCalcMinSize(WidgetT *w, const BitmapFontT *font) {
int32_t tabH = font->charHeight + TAB_PAD_V * 2;
int32_t maxPageW = 0;
int32_t maxPageH = 0;
int32_t tabHeaderW = 0;
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
if (c->type != WidgetTabPageE) {
continue;
}
widgetCalcMinSizeTree(c, font);
maxPageW = DVX_MAX(maxPageW, c->calcMinW);
maxPageH = DVX_MAX(maxPageH, c->calcMinH);
int32_t labelW = (int32_t)strlen(c->as.tabPage.title) * font->charWidth + TAB_PAD_H * 2;
tabHeaderW += labelW;
}
w->calcMinW = DVX_MAX(maxPageW + TAB_BORDER * 2, tabHeaderW);
w->calcMinH = tabH + maxPageH + TAB_BORDER * 2;
}
// ============================================================
// widgetTabControlLayout
// ============================================================
void widgetTabControlLayout(WidgetT *w, const BitmapFontT *font) {
int32_t tabH = font->charHeight + TAB_PAD_V * 2;
int32_t contentX = w->x + TAB_BORDER;
int32_t contentY = w->y + tabH + TAB_BORDER;
int32_t contentW = w->w - TAB_BORDER * 2;
int32_t contentH = w->h - tabH - TAB_BORDER * 2;
if (contentW < 0) { contentW = 0; }
if (contentH < 0) { contentH = 0; }
int32_t idx = 0;
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
if (c->type != WidgetTabPageE) {
continue;
}
c->x = contentX;
c->y = contentY;
c->w = contentW;
c->h = contentH;
// Only layout the active page
if (idx == w->as.tabControl.activeTab) {
widgetLayoutChildren(c, font);
}
idx++;
}
}
// ============================================================
// widgetTabControlOnMouse
// ============================================================
void widgetTabControlOnMouse(WidgetT *hit, WidgetT *root, int32_t vx, int32_t vy) {
AppContextT *ctx = (AppContextT *)root->userData;
const BitmapFontT *font = &ctx->font;
int32_t tabH = font->charHeight + TAB_PAD_V * 2;
// Only handle clicks in the tab header area
if (vy >= hit->y && vy < hit->y + tabH) {
int32_t tabX = hit->x + 2;
int32_t tabIdx = 0;
for (WidgetT *c = hit->firstChild; c; c = c->nextSibling) {
if (c->type != WidgetTabPageE) {
continue;
}
int32_t tw = (int32_t)strlen(c->as.tabPage.title) * font->charWidth + TAB_PAD_H * 2;
if (vx >= tabX && vx < tabX + tw) {
if (tabIdx != hit->as.tabControl.activeTab) {
hit->as.tabControl.activeTab = tabIdx;
if (hit->onChange) {
hit->onChange(hit);
}
}
break;
}
tabX += tw;
tabIdx++;
}
}
}
// ============================================================
// widgetTabControlPaint
// ============================================================
void widgetTabControlPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
int32_t tabH = font->charHeight + TAB_PAD_V * 2;
// Content panel
BevelStyleT panelBevel;
panelBevel.highlight = colors->windowHighlight;
panelBevel.shadow = colors->windowShadow;
panelBevel.face = colors->contentBg;
panelBevel.width = 2;
drawBevel(d, ops, w->x, w->y + tabH, w->w, w->h - tabH, &panelBevel);
// Tab headers
int32_t tabX = w->x + 2;
int32_t tabIdx = 0;
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
if (c->type != WidgetTabPageE) {
continue;
}
int32_t tw = (int32_t)strlen(c->as.tabPage.title) * font->charWidth + TAB_PAD_H * 2;
bool isActive = (tabIdx == w->as.tabControl.activeTab);
int32_t ty = isActive ? w->y : w->y + 2;
int32_t th = isActive ? tabH + 2 : tabH;
uint32_t tabFace = isActive ? colors->contentBg : colors->windowFace;
// Fill tab background
rectFill(d, ops, tabX + 2, ty + 2, tw - 4, th - 2, tabFace);
// Top edge
drawHLine(d, ops, tabX + 2, ty, tw - 4, colors->windowHighlight);
drawHLine(d, ops, tabX + 2, ty + 1, tw - 4, colors->windowHighlight);
// Left edge
drawVLine(d, ops, tabX, ty + 2, th - 2, colors->windowHighlight);
drawVLine(d, ops, tabX + 1, ty + 2, th - 2, colors->windowHighlight);
// Right edge
drawVLine(d, ops, tabX + tw - 1, ty + 2, th - 2, colors->windowShadow);
drawVLine(d, ops, tabX + tw - 2, ty + 2, th - 2, colors->windowShadow);
if (isActive) {
// Erase panel top border under active tab
rectFill(d, ops, tabX + 2, w->y + tabH, tw - 4, 2, colors->contentBg);
} else {
// Bottom edge for inactive tab
drawHLine(d, ops, tabX, ty + th - 1, tw, colors->windowShadow);
drawHLine(d, ops, tabX + 1, ty + th - 2, tw - 2, colors->windowShadow);
}
// Tab label
int32_t labelY = ty + TAB_PAD_V;
if (!isActive) {
labelY++;
}
drawText(d, ops, font, tabX + TAB_PAD_H, labelY,
c->as.tabPage.title, colors->contentFg, tabFace, true);
tabX += tw;
tabIdx++;
}
// Paint only active tab page's children
tabIdx = 0;
for (WidgetT *c = w->firstChild; c; c = c->nextSibling) {
if (c->type != WidgetTabPageE) {
continue;
}
if (tabIdx == w->as.tabControl.activeTab) {
for (WidgetT *gc = c->firstChild; gc; gc = gc->nextSibling) {
widgetPaintOne(gc, d, ops, font, colors);
}
break;
}
tabIdx++;
}
}