Skip to content

Commit 8b64b35

Browse files
committed
feat(button): add support for a badge
1 parent 2a75dce commit 8b64b35

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

src/ui/controls/button.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,37 @@ void Button::setCursorShape(std::uint32_t shape) {
273273
}
274274
}
275275

276+
void Button::setBadge(std::string_view text) {
277+
ensureBadge();
278+
m_badgeLabel->setText(text);
279+
m_badge->setVisible(!text.empty());
280+
}
281+
282+
void Button::setBadgeFontSize(float size) {
283+
ensureBadge();
284+
m_badgeLabel->setFontSize(size);
285+
}
286+
287+
void Button::ensureBadge() {
288+
if (m_badge != nullptr) {
289+
return;
290+
}
291+
auto badge = std::make_unique<Flex>();
292+
badge->setDirection(FlexDirection::Horizontal);
293+
badge->setAlign(FlexAlign::Center);
294+
badge->setJustify(FlexJustify::Center);
295+
badge->setPadding(2.0f, Style::spaceXs);
296+
badge->setRadius(Style::scaledRadiusSm());
297+
badge->setParticipatesInLayout(false);
298+
badge->setVisible(false);
299+
300+
auto label = std::make_unique<Label>();
301+
label->setFontSize(Style::fontSizeCaption * 0.85f);
302+
m_badgeLabel = static_cast<Label*>(badge->addChild(std::move(label)));
303+
304+
m_badge = static_cast<Flex*>(addChild(std::move(badge)));
305+
}
306+
276307
void Button::updateInputArea() {
277308
if (m_inputArea != nullptr) {
278309
m_inputArea->setPosition(0.0f, 0.0f);
@@ -394,7 +425,7 @@ void Button::applyColors(const Color& bg, const Color& border, const Color& labe
394425
m_glyph->setColor(label);
395426
}
396427
for (auto& child : children()) {
397-
if (child.get() == m_label || child.get() == m_glyph) {
428+
if (child.get() == m_label || child.get() == m_glyph || child.get() == m_badge) {
398429
continue;
399430
}
400431
if (auto* lbl = dynamic_cast<Label*>(child.get())) {
@@ -403,6 +434,12 @@ void Button::applyColors(const Color& bg, const Color& border, const Color& labe
403434
gl->setColor(label);
404435
}
405436
}
437+
if (m_badge != nullptr) {
438+
m_badge->setFill(Color{label.r, label.g, label.b, label.a * 0.85f});
439+
if (m_badgeLabel != nullptr) {
440+
m_badgeLabel->setColor(bg);
441+
}
442+
}
406443
m_visualStateInitialized = true;
407444
}
408445

@@ -565,6 +602,16 @@ void Button::doLayout(Renderer& renderer) {
565602
m_inputArea->setSize(width(), height());
566603
}
567604

605+
if (m_badge != nullptr && m_badge->visible()) {
606+
m_badgeLabel->measure(renderer);
607+
m_badge->setMinWidth(0.0f);
608+
m_badge->layout(renderer);
609+
m_badge->setMinWidth(m_badge->height());
610+
m_badge->layout(renderer);
611+
const float margin = Style::spaceXs;
612+
m_badge->setPosition(width() - m_badge->width() - margin, margin);
613+
}
614+
568615
// Only apply visual state if no animation is in progress — a running
569616
// animation already owns the color transition and re-calling here would
570617
// reset its from/to snapshot, collapsing the animation to a no-op.

src/ui/controls/button.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
class AnimationManager;
1212

13+
class Flex;
1314
class Glyph;
1415
class InputArea;
1516
class Label;
@@ -68,6 +69,8 @@ class Button : public Flex {
6869
void setOnLeave(std::function<void()> callback);
6970
void setHoverSuppressed(bool suppressed);
7071
void setCursorShape(std::uint32_t shape);
72+
void setBadge(std::string_view text);
73+
void setBadgeFontSize(float size);
7174

7275
// Call after layout() to sync InputArea bounds
7376
void updateInputArea();
@@ -91,8 +94,12 @@ class Button : public Flex {
9194

9295
void applyColors(const Color& bg, const Color& border, const Color& label);
9396

97+
void ensureBadge();
98+
9499
Glyph* m_glyph = nullptr;
95100
Label* m_label = nullptr;
101+
Flex* m_badge = nullptr;
102+
Label* m_badgeLabel = nullptr;
96103
InputArea* m_inputArea = nullptr;
97104
std::uint32_t m_animId = 0;
98105
std::function<void()> m_onClick;

0 commit comments

Comments
 (0)