// 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); } } wgtInvalidate(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); } wgtInvalidate(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); } wgtInvalidate(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 radio box BevelStyleT bevel; bevel.highlight = colors->windowShadow; bevel.shadow = colors->windowHighlight; bevel.face = bg; bevel.width = 1; drawBevel(d, ops, w->x, boxY, CHECKBOX_BOX_SIZE, CHECKBOX_BOX_SIZE, &bevel); // Draw filled dot if selected if (w->parent && w->parent->type == WidgetRadioGroupE && w->parent->as.radioGroup.selectedIdx == w->as.radio.index) { rectFill(d, ops, w->x + 3, boxY + 3, CHECKBOX_BOX_SIZE - 6, CHECKBOX_BOX_SIZE - 6, 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); } }