UI: Let RadioButton show a hint, like CheckBox

RadioButton is now a ClickableItem, so it fills its parent's width by
default and draws its hint text below the title, lined up with the text
after the radio circle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-11-05 18:20:33 +01:00
1 parent 1085fcc820
commit 94a65a1ff7
2 files changed
+45 -31

No files matched your search

+18 -9
View File
@@ -470,7 +470,7 @@ void ClickableItem::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec
if (hintText_.empty()) {
return;
}
float availWidth = measuredWidth_ - HINT_PADDING_X * 2;
float availWidth = measuredWidth_ - HintIndent() - HINT_PADDING_X;
if (availWidth <= 0.0f) {
availWidth = MAX_ITEM_SIZE;
}
@@ -487,11 +487,16 @@ void ClickableItem::DrawHint(UIContext &dc, const Style &style) const {
dc.SetFontStyle(dc.GetTheme().uiFontTiny);
dc.SetFontScale(1.0f, 1.0f);
const float textHeight = hintHeight_ - HINT_PADDING_BOTTOM + HINT_TITLE_OVERLAP;
Bounds hintBounds(bounds_.x + HINT_PADDING_X, bounds_.y2() - HINT_PADDING_BOTTOM - textHeight, bounds_.w - HINT_PADDING_X * 2, textHeight);
const float indent = HintIndent();
Bounds hintBounds(bounds_.x + indent, bounds_.y2() - HINT_PADDING_BOTTOM - textHeight, bounds_.w - indent - HINT_PADDING_X, textHeight);
dc.DrawTextRect(hintText_, hintBounds, style.fgColor, ALIGN_TOPLEFT | FLAG_WRAP_TEXT);
dc.SetFontStyle(dc.GetTheme().uiFont);
}
float ClickableItem::HintIndent() const {
return HINT_PADDING_X;
}
void Choice::ClickInternal() {
ClickableItem::ClickInternal();
UI::PlayUISound(UI::UISound::CONFIRM);
@@ -1097,11 +1102,13 @@ void RadioButton::Draw(UIContext &dc) {
DrawBG(dc, style, dc.GetTheme().choiceRoundRadius);
const Bounds bounds = ContentBounds();
dc.Flush();
dc.BeginNoTex();
dc.Draw()->Circle(bounds_.x + paddingW_ + radioRadius_, bounds_.centerY(), radioRadius_, 2.5f, 36, 0, style.fgColor, 1.0f);
dc.Draw()->Circle(bounds.x + paddingW_ + radioRadius_, bounds.centerY(), radioRadius_, 2.5f, 36, 0, style.fgColor, 1.0f);
if (checked) {
dc.Draw()->FillCircle(bounds_.x + paddingW_ + radioRadius_, bounds_.centerY(), radioInnerRadius_, 36, style.fgColor);
dc.Draw()->FillCircle(bounds.x + paddingW_ + radioRadius_, bounds.centerY(), radioInnerRadius_, 36, style.fgColor);
}
dc.Flush();
dc.Begin();
@@ -1109,20 +1116,22 @@ void RadioButton::Draw(UIContext &dc) {
float tw, th;
dc.MeasureText(dc.GetTheme().uiFont, 1.0f, 1.0f, text_, &tw, &th);
if (tw > bounds_.w) {
dc.PushScissor(bounds_);
if (tw > bounds.w) {
dc.PushScissor(bounds);
}
dc.SetFontStyle(dc.GetTheme().uiFont);
if (!text_.empty()) {
float textX = bounds_.x + paddingW_ * 2.0f + radioRadius_ * 2.0f;
dc.DrawText(text_, textX, bounds_.centerY(), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER);
float textX = bounds.x + paddingW_ * 2.0f + radioRadius_ * 2.0f;
dc.DrawText(text_, textX, bounds.centerY(), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER);
}
if (tw > bounds_.w) {
if (tw > bounds.w) {
dc.PopScissor();
}
DrawHint(dc, style);
}
ImageView::ImageView(ImageID atlasImage, const std::string &text, LayoutParams *layoutParams)
+27 -22
View File
@@ -564,7 +564,6 @@ public:
bool CanBeFocused() const override { return false; }
};
// All these light up their background when touched, or have focus.
class Clickable : public View {
public:
@@ -639,26 +638,6 @@ private:
bool ignoreText_ = false;
};
class RadioButton : public Clickable {
public:
RadioButton(int *value, int thisButtonValue, std::string_view text, LayoutParams *layoutParams = 0)
: Clickable(layoutParams), value_(value), thisButtonValue_(thisButtonValue), text_(text) {}
void Draw(UIContext &dc) override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
std::string DescribeText() const override;
private:
void ClickInternal() override;
int *value_;
int thisButtonValue_;
std::string text_;
const float paddingW_ = 8;
const float paddingH_ = 4;
const float radioRadius_ = 16.0f;
const float radioInnerRadius_ = 8.0f;
};
class Slider : public Clickable {
public:
Slider(int *value, int minValue, int maxValue, int step = 1, LayoutParams *layoutParams = nullptr)
@@ -768,7 +747,7 @@ public:
void Draw(UIContext &dc) override;
// Small explanatory text under the item, part of it (highlighted and clicked along with it).
// Drawn by Choice and CheckBox.
// Drawn by Choice and CheckBox and RadioButton.
void SetHintText(std::string_view text) {
hintText_ = text;
}
@@ -784,6 +763,9 @@ protected:
return b;
}
void DrawHint(UIContext &dc, const Style &style) const;
// Where the hint starts, from the item's left edge. Items whose text doesn't start at the
// default padding override this, to line the hint up with it.
virtual float HintIndent() const;
std::string hintText_;
float hintHeight_ = 0.0f;
@@ -1013,6 +995,29 @@ protected:
ImageID imageID_;
};
class RadioButton : public ClickableItem {
public:
RadioButton(int *value, int thisButtonValue, std::string_view text, LayoutParams *layoutParams = nullptr)
: ClickableItem(layoutParams), value_(value), thisButtonValue_(thisButtonValue), text_(text) {}
void Draw(UIContext &dc) override;
void GetContentDimensions(const UIContext &dc, float &w, float &h) const override;
std::string DescribeText() const override;
protected:
float HintIndent() const override { return paddingW_ * 2.0f + radioRadius_ * 2.0f; }
private:
void ClickInternal() override;
int *value_;
int thisButtonValue_;
std::string text_;
const float paddingW_ = 8;
const float paddingH_ = 4;
const float radioRadius_ = 16.0f;
const float radioInnerRadius_ = 8.0f;
};
class CollapsibleHeader : public CheckBox {
public:
CollapsibleHeader(bool *open, std::string_view text, LayoutParams *layoutParams = nullptr);