227 lines
6.9 KiB
C
227 lines
6.9 KiB
C
// widgetRadio.c — RadioGroup and Radio button widgets
|
|
|
|
#include "widgetInternal.h"
|
|
|
|
|
|
// ============================================================
|
|
// wgtRadio
|
|
// ============================================================
|
|
|
|
WidgetT *wgtRadio(WidgetT *parent, const char *text) {
|
|
WidgetT *w = widgetAlloc(parent, WidgetRadioE);
|
|
|
|
if (w) {
|
|
w->as.radio.text = text;
|
|
w->accelKey = accelParse(text);
|
|
|
|
// Auto-assign index based on position in parent
|
|
int32_t idx = 0;
|
|
|
|
for (WidgetT *c = parent->firstChild; c != w; c = c->nextSibling) {
|
|
if (c->type == WidgetRadioE) {
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
w->as.radio.index = idx;
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// wgtRadioGroup
|
|
// ============================================================
|
|
|
|
WidgetT *wgtRadioGroup(WidgetT *parent) {
|
|
WidgetT *w = widgetAlloc(parent, WidgetRadioGroupE);
|
|
|
|
if (w) {
|
|
w->as.radioGroup.selectedIdx = 0;
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetRadioGetText
|
|
// ============================================================
|
|
|
|
const char *widgetRadioGetText(const WidgetT *w) {
|
|
return w->as.radio.text;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetRadioSetText
|
|
// ============================================================
|
|
|
|
void widgetRadioSetText(WidgetT *w, const char *text) {
|
|
w->as.radio.text = text;
|
|
w->accelKey = accelParse(text);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetRadioCalcMinSize
|
|
// ============================================================
|
|
|
|
void widgetRadioCalcMinSize(WidgetT *w, const BitmapFontT *font) {
|
|
w->calcMinW = CHECKBOX_BOX_SIZE + CHECKBOX_GAP +
|
|
textWidthAccel(font, w->as.radio.text);
|
|
w->calcMinH = DVX_MAX(CHECKBOX_BOX_SIZE, font->charHeight);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetRadioOnKey
|
|
// ============================================================
|
|
|
|
void widgetRadioOnKey(WidgetT *w, int32_t key, int32_t mod) {
|
|
(void)mod;
|
|
|
|
if (key == ' ' || key == 0x0D) {
|
|
// Select this radio
|
|
if (w->parent && w->parent->type == WidgetRadioGroupE) {
|
|
w->parent->as.radioGroup.selectedIdx = w->as.radio.index;
|
|
|
|
if (w->parent->onChange) {
|
|
w->parent->onChange(w->parent);
|
|
}
|
|
}
|
|
|
|
wgtInvalidatePaint(w);
|
|
} else if (key == (0x50 | 0x100) || key == (0x4D | 0x100)) {
|
|
// Down or Right — next radio in group
|
|
if (w->parent && w->parent->type == WidgetRadioGroupE) {
|
|
WidgetT *next = NULL;
|
|
|
|
for (WidgetT *s = w->nextSibling; s; s = s->nextSibling) {
|
|
if (s->type == WidgetRadioE && s->visible && s->enabled) {
|
|
next = s;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (next) {
|
|
w->focused = false;
|
|
next->focused = true;
|
|
sFocusedWidget = next;
|
|
next->parent->as.radioGroup.selectedIdx = next->as.radio.index;
|
|
|
|
if (next->parent->onChange) {
|
|
next->parent->onChange(next->parent);
|
|
}
|
|
|
|
wgtInvalidatePaint(next);
|
|
}
|
|
}
|
|
} else if (key == (0x48 | 0x100) || key == (0x4B | 0x100)) {
|
|
// Up or Left — previous radio in group
|
|
if (w->parent && w->parent->type == WidgetRadioGroupE) {
|
|
WidgetT *prev = NULL;
|
|
|
|
for (WidgetT *s = w->parent->firstChild; s && s != w; s = s->nextSibling) {
|
|
if (s->type == WidgetRadioE && s->visible && s->enabled) {
|
|
prev = s;
|
|
}
|
|
}
|
|
|
|
if (prev) {
|
|
w->focused = false;
|
|
prev->focused = true;
|
|
sFocusedWidget = prev;
|
|
prev->parent->as.radioGroup.selectedIdx = prev->as.radio.index;
|
|
|
|
if (prev->parent->onChange) {
|
|
prev->parent->onChange(prev->parent);
|
|
}
|
|
|
|
wgtInvalidatePaint(prev);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetRadioOnMouse
|
|
// ============================================================
|
|
|
|
void widgetRadioOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy) {
|
|
(void)root;
|
|
(void)vx;
|
|
(void)vy;
|
|
w->focused = true;
|
|
|
|
if (w->parent && w->parent->type == WidgetRadioGroupE) {
|
|
w->parent->as.radioGroup.selectedIdx = w->as.radio.index;
|
|
|
|
if (w->parent->onChange) {
|
|
w->parent->onChange(w->parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// widgetRadioPaint
|
|
// ============================================================
|
|
|
|
void widgetRadioPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors) {
|
|
uint32_t fg = w->fgColor ? w->fgColor : colors->contentFg;
|
|
uint32_t bg = w->bgColor ? w->bgColor : colors->contentBg;
|
|
int32_t boxY = w->y + (w->h - CHECKBOX_BOX_SIZE) / 2;
|
|
|
|
// Draw diamond-shaped radio box
|
|
int32_t bx = w->x;
|
|
int32_t mid = CHECKBOX_BOX_SIZE / 2;
|
|
uint32_t hi = colors->windowShadow;
|
|
uint32_t sh = colors->windowHighlight;
|
|
|
|
// Fill interior
|
|
for (int32_t i = 0; i < CHECKBOX_BOX_SIZE; i++) {
|
|
int32_t dist = i < mid ? i : CHECKBOX_BOX_SIZE - 1 - i;
|
|
int32_t left = mid - dist;
|
|
int32_t right = mid + dist;
|
|
|
|
if (right > left) {
|
|
drawHLine(d, ops, bx + left + 1, boxY + i, right - left - 1, bg);
|
|
}
|
|
}
|
|
|
|
// Diamond border — upper-left edges get highlight, lower-right get shadow
|
|
for (int32_t i = 0; i < mid; i++) {
|
|
int32_t left = mid - i;
|
|
int32_t right = mid + i;
|
|
drawHLine(d, ops, bx + left, boxY + i, 1, hi);
|
|
drawHLine(d, ops, bx + right, boxY + i, 1, sh);
|
|
}
|
|
|
|
for (int32_t i = mid; i < CHECKBOX_BOX_SIZE; i++) {
|
|
int32_t left = mid - (CHECKBOX_BOX_SIZE - 1 - i);
|
|
int32_t right = mid + (CHECKBOX_BOX_SIZE - 1 - i);
|
|
drawHLine(d, ops, bx + left, boxY + i, 1, hi);
|
|
drawHLine(d, ops, bx + right, boxY + i, 1, sh);
|
|
}
|
|
|
|
// Draw filled diamond if selected
|
|
if (w->parent && w->parent->type == WidgetRadioGroupE &&
|
|
w->parent->as.radioGroup.selectedIdx == w->as.radio.index) {
|
|
for (int32_t i = -2; i <= 2; i++) {
|
|
int32_t span = 3 - (i < 0 ? -i : i);
|
|
drawHLine(d, ops, bx + mid - span, boxY + mid + i, span * 2 + 1, fg);
|
|
}
|
|
}
|
|
|
|
int32_t labelX = w->x + CHECKBOX_BOX_SIZE + CHECKBOX_GAP;
|
|
int32_t labelY = w->y + (w->h - font->charHeight) / 2;
|
|
int32_t labelW = textWidthAccel(font, w->as.radio.text);
|
|
drawTextAccel(d, ops, font, labelX, labelY, w->as.radio.text, fg, bg, false);
|
|
|
|
if (w->focused) {
|
|
drawFocusRect(d, ops, labelX - 1, labelY - 1, labelW + 2, font->charHeight + 2, fg);
|
|
}
|
|
}
|