mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
feat(controls): removed IconButton and added an optional icon to Button (less code to maintain)
This commit is contained in:
@@ -224,7 +224,6 @@ add_executable(noctalia
|
||||
src/ui/controls/Chip.cpp
|
||||
src/ui/controls/Dropdown.cpp
|
||||
src/ui/controls/Icon.cpp
|
||||
src/ui/controls/IconButton.cpp
|
||||
src/ui/controls/Label.cpp
|
||||
src/ui/controls/Slider.cpp
|
||||
src/ui/controls/Toggle.cpp
|
||||
|
||||
@@ -80,8 +80,11 @@ just build
|
||||
just rebuild # debug
|
||||
just rebuild release # release
|
||||
|
||||
# Run
|
||||
# Run debug
|
||||
just run
|
||||
|
||||
# Run release
|
||||
just run release
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "ui/controls/Box.h"
|
||||
#include "ui/controls/Button.h"
|
||||
#include "ui/controls/Dropdown.h"
|
||||
#include "ui/controls/IconButton.h"
|
||||
#include "ui/controls/Label.h"
|
||||
#include "ui/controls/Slider.h"
|
||||
#include "ui/controls/Toggle.h"
|
||||
@@ -51,16 +50,16 @@ void TestPanelContent::create(Renderer& renderer) {
|
||||
container->addChild(std::move(row));
|
||||
}
|
||||
|
||||
auto iconButton = std::make_unique<IconButton>();
|
||||
auto iconButton = std::make_unique<Button>();
|
||||
iconButton->setText("Settings");
|
||||
iconButton->setIcon("settings");
|
||||
iconButton->setVariant(ButtonVariant::Outline);
|
||||
iconButton->setVariant(ButtonVariant::Default);
|
||||
iconButton->setOnClick([]() {});
|
||||
m_iconButton = iconButton.get();
|
||||
{
|
||||
auto row = makeRow();
|
||||
auto rowLabel = std::make_unique<Label>();
|
||||
rowLabel->setText("IconButton");
|
||||
rowLabel->setText("Button w/ Icon");
|
||||
rowLabel->setCaptionStyle();
|
||||
row->addChild(std::move(rowLabel));
|
||||
row->addChild(std::move(iconButton));
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
class Box;
|
||||
class Button;
|
||||
class Dropdown;
|
||||
class IconButton;
|
||||
class Label;
|
||||
class Slider;
|
||||
class Toggle;
|
||||
@@ -25,7 +24,7 @@ private:
|
||||
Label* m_sliderValueLabel = nullptr;
|
||||
Button* m_button = nullptr;
|
||||
Dropdown* m_dropdown = nullptr;
|
||||
IconButton* m_iconButton = nullptr;
|
||||
Button* m_iconButton = nullptr;
|
||||
Slider* m_slider = nullptr;
|
||||
Toggle* m_toggle = nullptr;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ui/controls/Button.h"
|
||||
|
||||
#include "render/scene/InputArea.h"
|
||||
#include "ui/controls/Icon.h"
|
||||
#include "ui/controls/Label.h"
|
||||
#include "ui/style/Palette.h"
|
||||
#include "ui/style/Style.h"
|
||||
@@ -19,7 +20,22 @@ Button::Button() {
|
||||
|
||||
void Button::setText(std::string_view text) { m_label->setText(text); }
|
||||
|
||||
void Button::setFontSize(float size) { m_label->setFontSize(size); }
|
||||
void Button::setIcon(std::string_view name) {
|
||||
ensureIcon();
|
||||
m_icon->setIcon(name);
|
||||
}
|
||||
|
||||
void Button::setFontSize(float size) {
|
||||
m_label->setFontSize(size);
|
||||
if (m_icon != nullptr) {
|
||||
m_icon->setSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::setIconSize(float size) {
|
||||
ensureIcon();
|
||||
m_icon->setSize(size);
|
||||
}
|
||||
|
||||
void Button::setOnClick(std::function<void()> callback) {
|
||||
m_onClick = std::move(callback);
|
||||
@@ -134,22 +150,47 @@ void Button::applyVariant() {
|
||||
applyVisualState();
|
||||
}
|
||||
|
||||
void Button::ensureIcon() {
|
||||
if (m_icon != nullptr) {
|
||||
return;
|
||||
}
|
||||
// Insert icon before the label so it appears on the left
|
||||
auto& kids = children();
|
||||
std::size_t labelIndex = 0;
|
||||
for (std::size_t i = 0; i < kids.size(); ++i) {
|
||||
if (kids[i].get() == m_label) {
|
||||
labelIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto icon = std::make_unique<Icon>();
|
||||
m_icon = static_cast<Icon*>(insertChildAt(labelIndex, std::move(icon)));
|
||||
setDirection(BoxDirection::Horizontal);
|
||||
setGap(Style::spaceXs);
|
||||
}
|
||||
|
||||
void Button::applyVisualState() {
|
||||
bool isHovered = hovered();
|
||||
bool isPressed = pressed();
|
||||
|
||||
Color labelColor;
|
||||
if (isPressed) {
|
||||
setBackground(m_bgColorPressed);
|
||||
setBorderColor(m_borderColorPressed);
|
||||
m_label->setColor(m_labelColorPressed);
|
||||
labelColor = m_labelColorPressed;
|
||||
} else if (isHovered) {
|
||||
setBackground(m_bgColorHover);
|
||||
setBorderColor(m_borderColorHover);
|
||||
m_label->setColor(m_labelColorHover);
|
||||
labelColor = m_labelColorHover;
|
||||
} else {
|
||||
setBackground(m_bgColorNormal);
|
||||
setBorderColor(m_borderColorNormal);
|
||||
m_label->setColor(m_labelColorNormal);
|
||||
labelColor = m_labelColorNormal;
|
||||
}
|
||||
|
||||
m_label->setColor(labelColor);
|
||||
if (m_icon != nullptr) {
|
||||
m_icon->setColor(labelColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +200,9 @@ void Button::layout(Renderer& renderer) {
|
||||
}
|
||||
|
||||
m_label->measure(renderer);
|
||||
if (m_icon != nullptr) {
|
||||
m_icon->measure(renderer);
|
||||
}
|
||||
|
||||
if (m_inputArea != nullptr) {
|
||||
m_inputArea->setVisible(false);
|
||||
@@ -166,7 +210,10 @@ void Button::layout(Renderer& renderer) {
|
||||
|
||||
Box::layout(renderer);
|
||||
|
||||
m_label->setPosition((width() - m_label->width()) * 0.5f, (height() - m_label->height()) * 0.5f);
|
||||
// Center label when there's no icon
|
||||
if (m_icon == nullptr) {
|
||||
m_label->setPosition((width() - m_label->width()) * 0.5f, (height() - m_label->height()) * 0.5f);
|
||||
}
|
||||
|
||||
if (m_inputArea != nullptr) {
|
||||
m_inputArea->setVisible(true);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
class Icon;
|
||||
class InputArea;
|
||||
class Label;
|
||||
|
||||
@@ -23,7 +24,9 @@ public:
|
||||
Button();
|
||||
|
||||
void setText(std::string_view text);
|
||||
void setIcon(std::string_view name);
|
||||
void setFontSize(float size);
|
||||
void setIconSize(float size);
|
||||
void setVariant(ButtonVariant variant);
|
||||
void setOnClick(std::function<void()> callback);
|
||||
void setCursorShape(std::uint32_t shape);
|
||||
@@ -33,13 +36,16 @@ public:
|
||||
void updateInputArea();
|
||||
|
||||
[[nodiscard]] Label* label() const noexcept { return m_label; }
|
||||
[[nodiscard]] Icon* icon() const noexcept { return m_icon; }
|
||||
[[nodiscard]] bool hovered() const noexcept;
|
||||
[[nodiscard]] bool pressed() const noexcept;
|
||||
|
||||
private:
|
||||
void ensureIcon();
|
||||
void applyVariant();
|
||||
void applyVisualState();
|
||||
|
||||
Icon* m_icon = nullptr;
|
||||
Label* m_label = nullptr;
|
||||
InputArea* m_inputArea = nullptr;
|
||||
std::function<void()> m_onClick;
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
#include "ui/controls/IconButton.h"
|
||||
|
||||
#include "render/scene/InputArea.h"
|
||||
#include "ui/controls/Icon.h"
|
||||
#include "ui/controls/Label.h"
|
||||
#include "ui/style/Palette.h"
|
||||
#include "ui/style/Style.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
IconButton::IconButton() {
|
||||
setDirection(BoxDirection::Horizontal);
|
||||
setAlign(BoxAlign::Center);
|
||||
setGap(Style::spaceXs);
|
||||
setPadding(Style::paddingV, Style::paddingH, Style::paddingV, Style::paddingH);
|
||||
setRadius(Style::radiusMd);
|
||||
|
||||
auto icon = std::make_unique<Icon>();
|
||||
m_icon = static_cast<Icon*>(addChild(std::move(icon)));
|
||||
|
||||
auto label = std::make_unique<Label>();
|
||||
m_label = static_cast<Label*>(addChild(std::move(label)));
|
||||
|
||||
applyVariant();
|
||||
}
|
||||
|
||||
void IconButton::setText(std::string_view text) { m_label->setText(text); }
|
||||
|
||||
void IconButton::setIcon(std::string_view name) { m_icon->setIcon(name); }
|
||||
|
||||
void IconButton::setFontSize(float size) {
|
||||
m_label->setFontSize(size);
|
||||
m_icon->setSize(size);
|
||||
}
|
||||
|
||||
void IconButton::setIconSize(float size) { m_icon->setSize(size); }
|
||||
|
||||
void IconButton::setOnClick(std::function<void()> callback) {
|
||||
m_onClick = std::move(callback);
|
||||
|
||||
if (m_inputArea == nullptr) {
|
||||
auto area = std::make_unique<InputArea>();
|
||||
area->setOnEnter([this](const InputArea::PointerData& /*data*/) { applyVisualState(); });
|
||||
area->setOnLeave([this]() { applyVisualState(); });
|
||||
area->setOnPress([this](const InputArea::PointerData& /*data*/) { applyVisualState(); });
|
||||
area->setOnClick([this](const InputArea::PointerData& /*data*/) {
|
||||
if (m_onClick) {
|
||||
m_onClick();
|
||||
}
|
||||
applyVisualState();
|
||||
});
|
||||
m_inputArea = static_cast<InputArea*>(addChild(std::move(area)));
|
||||
m_inputArea->setPosition(0.0f, 0.0f);
|
||||
m_inputArea->setSize(width(), height());
|
||||
}
|
||||
}
|
||||
|
||||
void IconButton::setCursorShape(std::uint32_t shape) {
|
||||
if (m_inputArea != nullptr) {
|
||||
m_inputArea->setCursorShape(shape);
|
||||
}
|
||||
}
|
||||
|
||||
bool IconButton::hovered() const noexcept { return m_inputArea != nullptr && m_inputArea->hovered(); }
|
||||
|
||||
bool IconButton::pressed() const noexcept { return m_inputArea != nullptr && m_inputArea->pressed(); }
|
||||
|
||||
void IconButton::setVariant(ButtonVariant variant) {
|
||||
if (m_variant == variant) {
|
||||
return;
|
||||
}
|
||||
m_variant = variant;
|
||||
applyVariant();
|
||||
}
|
||||
|
||||
void IconButton::applyVariant() {
|
||||
setPadding(Style::paddingV, Style::paddingH, Style::paddingV, Style::paddingH);
|
||||
setRadius(Style::radiusMd);
|
||||
setBorderWidth(Style::borderWidth);
|
||||
|
||||
switch (m_variant) {
|
||||
case ButtonVariant::Default:
|
||||
m_bgColorNormal = palette.surfaceVariant;
|
||||
m_bgColorHover = brighten(m_bgColorNormal, 1.14f);
|
||||
m_bgColorPressed = palette.primary;
|
||||
m_labelColorNormal = palette.onSurfaceVariant;
|
||||
m_labelColorHover = palette.onSurfaceVariant;
|
||||
m_labelColorPressed = palette.onPrimary;
|
||||
m_borderColorNormal = palette.outline;
|
||||
m_borderColorHover = brighten(m_borderColorNormal, 1.14f);
|
||||
m_borderColorPressed = palette.primary;
|
||||
break;
|
||||
case ButtonVariant::Secondary:
|
||||
m_bgColorNormal = palette.secondary;
|
||||
m_bgColorHover = brighten(m_bgColorNormal, 1.12f);
|
||||
m_bgColorPressed = palette.primary;
|
||||
m_labelColorNormal = palette.onSecondary;
|
||||
m_labelColorHover = palette.onSecondary;
|
||||
m_labelColorPressed = palette.onPrimary;
|
||||
m_borderColorNormal = palette.outline;
|
||||
m_borderColorHover = brighten(m_borderColorNormal, 1.14f);
|
||||
m_borderColorPressed = palette.primary;
|
||||
break;
|
||||
case ButtonVariant::Destructive:
|
||||
m_bgColorNormal = palette.error;
|
||||
m_bgColorHover = brighten(m_bgColorNormal, 1.1f);
|
||||
m_bgColorPressed = palette.error;
|
||||
m_labelColorNormal = palette.onError;
|
||||
m_labelColorHover = palette.onError;
|
||||
m_labelColorPressed = palette.onError;
|
||||
m_borderColorNormal = palette.outline;
|
||||
m_borderColorHover = brighten(m_borderColorNormal, 1.14f);
|
||||
m_borderColorPressed = palette.error;
|
||||
break;
|
||||
case ButtonVariant::Outline:
|
||||
m_bgColorNormal = palette.surface;
|
||||
m_bgColorHover = brighten(m_bgColorNormal, 1.12f);
|
||||
m_bgColorPressed = palette.primary;
|
||||
m_labelColorNormal = palette.onSurface;
|
||||
m_labelColorHover = palette.onSurface;
|
||||
m_labelColorPressed = palette.onPrimary;
|
||||
m_borderColorNormal = palette.outline;
|
||||
m_borderColorHover = brighten(m_borderColorNormal, 1.14f);
|
||||
m_borderColorPressed = palette.primary;
|
||||
break;
|
||||
case ButtonVariant::Ghost:
|
||||
m_bgColorNormal = palette.surface;
|
||||
m_bgColorHover = brighten(m_bgColorNormal, 1.12f);
|
||||
m_bgColorPressed = palette.primary;
|
||||
m_labelColorNormal = palette.onSurface;
|
||||
m_labelColorHover = palette.onSurface;
|
||||
m_labelColorPressed = palette.onPrimary;
|
||||
m_borderColorNormal = palette.outline;
|
||||
m_borderColorHover = brighten(m_borderColorNormal, 1.14f);
|
||||
m_borderColorPressed = palette.primary;
|
||||
break;
|
||||
}
|
||||
|
||||
applyVisualState();
|
||||
}
|
||||
|
||||
void IconButton::applyVisualState() {
|
||||
const bool isHovered = hovered();
|
||||
const bool isPressed = pressed();
|
||||
|
||||
if (isPressed) {
|
||||
setBackground(m_bgColorPressed);
|
||||
setBorderColor(m_borderColorPressed);
|
||||
m_label->setColor(m_labelColorPressed);
|
||||
m_icon->setColor(m_labelColorPressed);
|
||||
} else if (isHovered) {
|
||||
setBackground(m_bgColorHover);
|
||||
setBorderColor(m_borderColorHover);
|
||||
m_label->setColor(m_labelColorHover);
|
||||
m_icon->setColor(m_labelColorHover);
|
||||
} else {
|
||||
setBackground(m_bgColorNormal);
|
||||
setBorderColor(m_borderColorNormal);
|
||||
m_label->setColor(m_labelColorNormal);
|
||||
m_icon->setColor(m_labelColorNormal);
|
||||
}
|
||||
}
|
||||
|
||||
void IconButton::layout(Renderer& renderer) {
|
||||
if (m_label == nullptr || m_icon == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_icon->measure(renderer);
|
||||
m_label->measure(renderer);
|
||||
|
||||
if (m_inputArea != nullptr) {
|
||||
m_inputArea->setVisible(false);
|
||||
}
|
||||
|
||||
Box::layout(renderer);
|
||||
|
||||
if (m_inputArea != nullptr) {
|
||||
m_inputArea->setVisible(true);
|
||||
m_inputArea->setPosition(0.0f, 0.0f);
|
||||
m_inputArea->setSize(width(), height());
|
||||
}
|
||||
|
||||
applyVisualState();
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/controls/Box.h"
|
||||
#include "ui/controls/Button.h"
|
||||
|
||||
#include "render/core/Color.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
class Icon;
|
||||
class InputArea;
|
||||
class Label;
|
||||
|
||||
class IconButton : public Box {
|
||||
public:
|
||||
IconButton();
|
||||
|
||||
void setText(std::string_view text);
|
||||
void setIcon(std::string_view name);
|
||||
void setFontSize(float size);
|
||||
void setIconSize(float size);
|
||||
void setVariant(ButtonVariant variant);
|
||||
void setOnClick(std::function<void()> callback);
|
||||
void setCursorShape(std::uint32_t shape);
|
||||
void layout(Renderer& renderer) override;
|
||||
|
||||
[[nodiscard]] Label* label() const noexcept { return m_label; }
|
||||
[[nodiscard]] Icon* icon() const noexcept { return m_icon; }
|
||||
[[nodiscard]] bool hovered() const noexcept;
|
||||
[[nodiscard]] bool pressed() const noexcept;
|
||||
|
||||
private:
|
||||
void applyVariant();
|
||||
void applyVisualState();
|
||||
|
||||
Icon* m_icon = nullptr;
|
||||
Label* m_label = nullptr;
|
||||
InputArea* m_inputArea = nullptr;
|
||||
std::function<void()> m_onClick;
|
||||
ButtonVariant m_variant = ButtonVariant::Default;
|
||||
|
||||
Color m_bgColorNormal{};
|
||||
Color m_bgColorHover{};
|
||||
Color m_bgColorPressed{};
|
||||
Color m_borderColorNormal{};
|
||||
Color m_borderColorHover{};
|
||||
Color m_borderColorPressed{};
|
||||
Color m_labelColorNormal{};
|
||||
Color m_labelColorHover{};
|
||||
Color m_labelColorPressed{};
|
||||
};
|
||||
Reference in New Issue
Block a user