mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
feat(controls): add checkbox
This commit is contained in:
@@ -415,6 +415,7 @@ add_executable(noctalia
|
||||
src/ui/controls/audio_spectrum.cpp
|
||||
src/ui/controls/button.cpp
|
||||
src/ui/controls/chip.cpp
|
||||
src/ui/controls/checkbox.cpp
|
||||
src/ui/controls/flex.cpp
|
||||
src/ui/controls/glyph.cpp
|
||||
src/ui/controls/glyph_registry.cpp
|
||||
|
||||
@@ -202,7 +202,7 @@ gdbus call --session --dest dev.noctalia.Debug --object-path /dev/noctalia/Debug
|
||||
|
||||
### Controls (`src/ui/controls/`)
|
||||
|
||||
- [ ] Checkbox
|
||||
- [x] Checkbox
|
||||
- [ ] Tab bar
|
||||
- [ ] Grid view
|
||||
- [ ] Context menu
|
||||
@@ -216,7 +216,6 @@ gdbus call --session --dest dev.noctalia.Debug --object-path /dev/noctalia/Debug
|
||||
- [ ] Network
|
||||
- [ ] Bluetooth
|
||||
- [ ] Brightness
|
||||
- [ ] Microphone <- unsure we need it
|
||||
- [ ] Power profile
|
||||
- [~] System monitor <- need graph fix
|
||||
- [ ] Dock
|
||||
@@ -227,7 +226,7 @@ gdbus call --session --dest dev.noctalia.Debug --object-path /dev/noctalia/Debug
|
||||
- [ ] Keep awake (idle inhibitor)
|
||||
- [ ] Audio visualizer
|
||||
- [x] Launcher button
|
||||
- [ ] Session menu button
|
||||
- [x] Session menu button
|
||||
- [ ] Wallpaper selector button
|
||||
- [ ] Custom button (with user-defined IPC)
|
||||
- [ ] Settings button
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "ui/controls/box.h"
|
||||
#include "ui/controls/button.h"
|
||||
#include "ui/controls/checkbox.h"
|
||||
#include "ui/controls/flex.h"
|
||||
#include "ui/controls/glyph.h"
|
||||
#include "ui/controls/input.h"
|
||||
@@ -203,6 +204,31 @@ void TestPanel::create(Renderer& renderer) {
|
||||
container->addChild(std::move(row));
|
||||
}
|
||||
|
||||
// Checkbox
|
||||
auto checkbox = std::make_unique<Checkbox>();
|
||||
checkbox->setScale(scale);
|
||||
checkbox->setChecked(true);
|
||||
checkbox->setOnChange([this](bool checked) {
|
||||
if (m_checkboxValueLabel != nullptr) {
|
||||
m_checkboxValueLabel->setText(checked ? "true" : "false");
|
||||
}
|
||||
});
|
||||
m_checkbox = checkbox.get();
|
||||
|
||||
auto checkboxValueLabel = std::make_unique<Label>();
|
||||
checkboxValueLabel->setText("true");
|
||||
checkboxValueLabel->setCaptionStyle();
|
||||
checkboxValueLabel->setFontSize(Style::fontSizeCaption * scale);
|
||||
m_checkboxValueLabel = checkboxValueLabel.get();
|
||||
|
||||
{
|
||||
auto row = makeRow();
|
||||
row->addChild(makeRowLabel("Checkbox", kRowLabelWidth));
|
||||
row->addChild(std::move(checkbox));
|
||||
row->addChild(std::move(checkboxValueLabel));
|
||||
container->addChild(std::move(row));
|
||||
}
|
||||
|
||||
// Radio
|
||||
{
|
||||
auto options = std::make_unique<Flex>();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
class Flex;
|
||||
class Button;
|
||||
class Box;
|
||||
class Checkbox;
|
||||
class Glyph;
|
||||
class Input;
|
||||
class RadioButton;
|
||||
@@ -20,8 +21,8 @@ public:
|
||||
void layout(Renderer& renderer, float width, float height) override;
|
||||
void update(Renderer& renderer) override;
|
||||
|
||||
[[nodiscard]] float preferredWidth() const override { return scaled(640.0f); }
|
||||
[[nodiscard]] float preferredHeight() const override { return scaled(480.0f); }
|
||||
[[nodiscard]] float preferredWidth() const override { return scaled(860.0f); }
|
||||
[[nodiscard]] float preferredHeight() const override { return scaled(620.0f); }
|
||||
// [[nodiscard]] bool centeredHorizontally() const override { return true; }
|
||||
// [[nodiscard]] bool centeredVertically() const override { return true; }
|
||||
private:
|
||||
@@ -29,6 +30,7 @@ private:
|
||||
Label* m_headerLabel = nullptr;
|
||||
Label* m_sliderValueLabel = nullptr;
|
||||
Label* m_toggleValueLabel = nullptr;
|
||||
Label* m_checkboxValueLabel = nullptr;
|
||||
Button* m_button = nullptr;
|
||||
Select* m_select = nullptr;
|
||||
Button* m_glyphTextButton = nullptr;
|
||||
@@ -37,6 +39,7 @@ private:
|
||||
Glyph* m_glyph = nullptr;
|
||||
Slider* m_slider = nullptr;
|
||||
Toggle* m_toggle = nullptr;
|
||||
Checkbox* m_checkbox = nullptr;
|
||||
RadioButton* m_radioA = nullptr;
|
||||
RadioButton* m_radioB = nullptr;
|
||||
Spinner* m_spinner = nullptr;
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
#include "ui/controls/checkbox.h"
|
||||
|
||||
#include "render/core/renderer.h"
|
||||
#include "render/scene/input_area.h"
|
||||
#include "ui/controls/box.h"
|
||||
#include "ui/controls/glyph.h"
|
||||
#include "ui/palette.h"
|
||||
#include "ui/style.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
|
||||
Checkbox::Checkbox() {
|
||||
auto box = std::make_unique<Box>();
|
||||
m_box = static_cast<Box*>(addChild(std::move(box)));
|
||||
|
||||
auto checkGlyph = std::make_unique<Glyph>();
|
||||
checkGlyph->setGlyph("check");
|
||||
m_checkGlyph = static_cast<Glyph*>(addChild(std::move(checkGlyph)));
|
||||
|
||||
auto area = std::make_unique<InputArea>();
|
||||
area->setOnEnter([this](const InputArea::PointerData& /*data*/) { applyState(); });
|
||||
area->setOnLeave([this]() { applyState(); });
|
||||
area->setOnPress([this](const InputArea::PointerData& /*data*/) { applyState(); });
|
||||
area->setOnClick([this](const InputArea::PointerData& /*data*/) {
|
||||
if (!m_enabled) {
|
||||
return;
|
||||
}
|
||||
const bool next = !m_checked;
|
||||
m_checked = next;
|
||||
applyState();
|
||||
if (m_onChange) {
|
||||
m_onChange(next);
|
||||
}
|
||||
});
|
||||
m_inputArea = static_cast<InputArea*>(addChild(std::move(area)));
|
||||
|
||||
applyState();
|
||||
}
|
||||
|
||||
void Checkbox::setChecked(bool checked) {
|
||||
if (m_checked == checked) {
|
||||
return;
|
||||
}
|
||||
m_checked = checked;
|
||||
applyState();
|
||||
}
|
||||
|
||||
void Checkbox::setEnabled(bool enabled) {
|
||||
if (m_enabled == enabled) {
|
||||
return;
|
||||
}
|
||||
m_enabled = enabled;
|
||||
if (m_inputArea != nullptr) {
|
||||
m_inputArea->setEnabled(enabled);
|
||||
}
|
||||
applyState();
|
||||
}
|
||||
|
||||
void Checkbox::setOnChange(std::function<void(bool)> callback) { m_onChange = std::move(callback); }
|
||||
|
||||
void Checkbox::setScale(float scale) {
|
||||
m_scale = std::max(0.1f, scale);
|
||||
applyState();
|
||||
markDirty();
|
||||
}
|
||||
|
||||
bool Checkbox::hovered() const noexcept { return m_inputArea != nullptr && m_inputArea->hovered(); }
|
||||
|
||||
bool Checkbox::pressed() const noexcept { return m_inputArea != nullptr && m_inputArea->pressed(); }
|
||||
|
||||
void Checkbox::layout(Renderer& renderer) {
|
||||
const float touchSize = Style::controlHeightSm * m_scale;
|
||||
const float boxSize = (Style::fontSizeTitle + Style::spaceXs) * m_scale;
|
||||
const float boxInset = (touchSize - boxSize) * 0.5f;
|
||||
|
||||
setSize(touchSize, touchSize);
|
||||
|
||||
if (m_box != nullptr) {
|
||||
m_box->setPosition(boxInset, boxInset);
|
||||
m_box->setSize(boxSize, boxSize);
|
||||
m_box->setRadius(Style::radiusSm * m_scale);
|
||||
m_box->setSoftness(1.0f);
|
||||
}
|
||||
|
||||
if (m_checkGlyph != nullptr) {
|
||||
m_checkGlyph->setGlyphSize((Style::fontSizeBody + Style::spaceXs * 0.5f) * m_scale);
|
||||
m_checkGlyph->measure(renderer);
|
||||
m_checkGlyph->setPosition(std::round(boxInset + (boxSize - m_checkGlyph->width()) * 0.5f),
|
||||
std::round(boxInset + (boxSize - m_checkGlyph->height()) * 0.5f));
|
||||
}
|
||||
|
||||
if (m_inputArea != nullptr) {
|
||||
m_inputArea->setPosition(0.0f, 0.0f);
|
||||
m_inputArea->setSize(width(), height());
|
||||
}
|
||||
}
|
||||
|
||||
void Checkbox::applyState() {
|
||||
if (m_box == nullptr || m_checkGlyph == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Color fill = palette.surface;
|
||||
Color border = palette.outline;
|
||||
if (m_checked) {
|
||||
fill = palette.primary;
|
||||
border = palette.primary;
|
||||
} else if (pressed() || hovered()) {
|
||||
border = palette.primary;
|
||||
}
|
||||
|
||||
m_box->setFill(fill);
|
||||
m_box->setBorder(border, Style::borderWidth * m_scale);
|
||||
|
||||
m_checkGlyph->setColor(palette.onPrimary);
|
||||
m_checkGlyph->setVisible(m_checked);
|
||||
|
||||
setOpacity(m_enabled ? 1.0f : 0.55f);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "render/scene/node.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class Box;
|
||||
class Glyph;
|
||||
class InputArea;
|
||||
class Renderer;
|
||||
|
||||
class Checkbox : public Node {
|
||||
public:
|
||||
Checkbox();
|
||||
|
||||
void setChecked(bool checked);
|
||||
void setEnabled(bool enabled);
|
||||
void setOnChange(std::function<void(bool)> callback);
|
||||
void setScale(float scale);
|
||||
|
||||
[[nodiscard]] bool checked() const noexcept { return m_checked; }
|
||||
[[nodiscard]] bool enabled() const noexcept { return m_enabled; }
|
||||
[[nodiscard]] bool hovered() const noexcept;
|
||||
[[nodiscard]] bool pressed() const noexcept;
|
||||
|
||||
void layout(Renderer& renderer) override;
|
||||
|
||||
private:
|
||||
void applyState();
|
||||
|
||||
Box* m_box = nullptr;
|
||||
Glyph* m_checkGlyph = nullptr;
|
||||
InputArea* m_inputArea = nullptr;
|
||||
std::function<void(bool)> m_onChange;
|
||||
bool m_checked = false;
|
||||
bool m_enabled = true;
|
||||
float m_scale = 1.0f;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user