mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
ui(controls): visual overhaul for stepper & segmented
This commit is contained in:
@@ -336,6 +336,15 @@ void Input::setFrameVisible(bool visible) {
|
||||
markPaintDirty();
|
||||
}
|
||||
|
||||
void Input::setEmbeddedOnSolidPrimary(bool embedded) {
|
||||
if (m_embeddedOnSolidPrimary == embedded) {
|
||||
return;
|
||||
}
|
||||
m_embeddedOnSolidPrimary = embedded;
|
||||
applyVisualState();
|
||||
markPaintDirty();
|
||||
}
|
||||
|
||||
void Input::setBold(bool bold) {
|
||||
if (m_label != nullptr) {
|
||||
m_label->setBold(bold);
|
||||
@@ -708,6 +717,39 @@ void Input::applyVisualState() {
|
||||
m_background->setVisible(false);
|
||||
}
|
||||
|
||||
if (m_embeddedOnSolidPrimary && !m_frameVisible) {
|
||||
auto selectionStyleEmb = m_selectionRect->style();
|
||||
selectionStyleEmb.fill = resolved(ColorRole::Surface, 0.4f);
|
||||
selectionStyleEmb.fillMode = FillMode::Solid;
|
||||
selectionStyleEmb.radius = 2.0f;
|
||||
m_selectionRect->setStyle(selectionStyleEmb);
|
||||
|
||||
auto cursorStyleEmb = m_cursor->style();
|
||||
cursorStyleEmb.fill = resolved(ColorRole::Surface);
|
||||
cursorStyleEmb.fillMode = FillMode::Solid;
|
||||
cursorStyleEmb.radius = 1.0f;
|
||||
m_cursor->setStyle(cursorStyleEmb);
|
||||
|
||||
if (m_invalid) {
|
||||
m_label->setColor(colorSpecFromRole(ColorRole::Error));
|
||||
} else if ((m_value.empty() && !m_placeholder.empty()) || readOnly) {
|
||||
m_label->setColor(colorSpecFromRole(ColorRole::OnPrimary, 0.65f));
|
||||
} else {
|
||||
m_label->setColor(colorSpecFromRole(ColorRole::OnPrimary));
|
||||
}
|
||||
const Color passwordGlyphEmb =
|
||||
m_invalid ? resolved(ColorRole::Error)
|
||||
: (((m_value.empty() && !m_placeholder.empty()) || readOnly) ? resolved(ColorRole::OnPrimary, 0.65f)
|
||||
: resolved(ColorRole::OnPrimary));
|
||||
for (auto* glyph : m_passwordGlyphs) {
|
||||
glyph->setColor(passwordGlyphEmb);
|
||||
}
|
||||
if (m_clearButtonGlyph != nullptr) {
|
||||
m_clearButtonGlyph->setColor(resolved(ColorRole::OnPrimary, clearButtonHovered ? 1.0f : 0.72f));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto selectionStyle = m_selectionRect->style();
|
||||
selectionStyle.fill = resolved(ColorRole::Primary);
|
||||
selectionStyle.fillMode = FillMode::Solid;
|
||||
|
||||
@@ -39,6 +39,8 @@ public:
|
||||
void setPasswordMode(bool enabled);
|
||||
void setInvalid(bool invalid);
|
||||
void setFrameVisible(bool visible);
|
||||
/// When the frame is hidden, treat the field as sitting on a solid Primary fill (e.g. segmented control center).
|
||||
void setEmbeddedOnSolidPrimary(bool embedded);
|
||||
void setBold(bool bold);
|
||||
void setMinLayoutWidth(float width);
|
||||
void setTextAlign(TextAlign align);
|
||||
@@ -129,6 +131,7 @@ private:
|
||||
bool m_passwordMode = false;
|
||||
bool m_invalid = false;
|
||||
bool m_frameVisible = true;
|
||||
bool m_embeddedOnSolidPrimary = false;
|
||||
float m_minLayoutWidth = 0.0f;
|
||||
float m_contentLeadSlack = 0.0f;
|
||||
TextAlign m_textAlign = TextAlign::Start;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "render/core/render_styles.h"
|
||||
#include "ui/controls/button.h"
|
||||
#include "ui/controls/flex.h"
|
||||
#include "ui/controls/separator.h"
|
||||
#include "ui/palette.h"
|
||||
#include "ui/style.h"
|
||||
|
||||
@@ -20,8 +22,15 @@ std::size_t Segmented::addOption(std::string_view label) { return addOption(labe
|
||||
|
||||
std::size_t Segmented::addOption(std::string_view label, std::string_view glyph) {
|
||||
const std::size_t index = m_buttons.size();
|
||||
Button* btn = makeSegmentButton(label, glyph, index);
|
||||
m_buttons.push_back(btn);
|
||||
if (index > 0) {
|
||||
auto sep = makeSegmentSeparator();
|
||||
m_separators.push_back(sep.get());
|
||||
addChild(std::move(sep));
|
||||
}
|
||||
auto btn = makeSegmentButton(label, glyph, index);
|
||||
Button* raw = btn.get();
|
||||
m_buttons.push_back(raw);
|
||||
addChild(std::move(btn));
|
||||
refreshVariants();
|
||||
return index;
|
||||
}
|
||||
@@ -60,13 +69,29 @@ void Segmented::setScale(float scale) {
|
||||
btn->setGlyphSize(fs);
|
||||
}
|
||||
}
|
||||
const float ruleW = std::max(1.0f, Style::borderWidth * m_scale);
|
||||
for (Separator* sep : m_separators) {
|
||||
if (sep != nullptr) {
|
||||
sep->setThickness(ruleW);
|
||||
}
|
||||
}
|
||||
refreshVariants();
|
||||
markLayoutDirty();
|
||||
}
|
||||
|
||||
void Segmented::setOnChange(std::function<void(std::size_t)> callback) { m_onChange = std::move(callback); }
|
||||
|
||||
Button* Segmented::makeSegmentButton(std::string_view label, std::string_view glyph, std::size_t index) {
|
||||
std::unique_ptr<Separator> Segmented::makeSegmentSeparator() {
|
||||
auto sep = std::make_unique<Separator>();
|
||||
sep->setOrientation(SeparatorOrientation::VerticalRule);
|
||||
sep->setThickness(std::max(1.0f, Style::borderWidth * m_scale));
|
||||
sep->setColor(colorSpecFromRole(ColorRole::Outline, 0.5f));
|
||||
sep->setFlexGrow(0.0f);
|
||||
return sep;
|
||||
}
|
||||
|
||||
std::unique_ptr<Button> Segmented::makeSegmentButton(std::string_view label, std::string_view glyph,
|
||||
std::size_t index) {
|
||||
auto btn = std::make_unique<Button>();
|
||||
if (!glyph.empty()) {
|
||||
btn->setGlyph(glyph);
|
||||
@@ -77,11 +102,9 @@ Button* Segmented::makeSegmentButton(std::string_view label, std::string_view gl
|
||||
btn->setMinHeight(Style::controlHeight * m_scale);
|
||||
btn->setPadding(Style::spaceXs * m_scale, Style::spaceMd * m_scale);
|
||||
btn->setOnClick([this, index]() { setSelectedIndex(index); });
|
||||
Button* raw = btn.get();
|
||||
raw->setFlexGrow(m_equalSegmentWidths ? 1.0f : 0.0f);
|
||||
raw->setContentAlign(ButtonContentAlign::Center);
|
||||
addChild(std::move(btn));
|
||||
return raw;
|
||||
btn->setFlexGrow(m_equalSegmentWidths ? 1.0f : 0.0f);
|
||||
btn->setContentAlign(ButtonContentAlign::Center);
|
||||
return btn;
|
||||
}
|
||||
|
||||
void Segmented::setEqualSegmentWidths(bool equalWidths) {
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class Button;
|
||||
class Separator;
|
||||
|
||||
class Segmented : public Flex {
|
||||
public:
|
||||
@@ -28,11 +30,14 @@ public:
|
||||
void setEqualSegmentWidths(bool equalWidths);
|
||||
|
||||
private:
|
||||
Button* makeSegmentButton(std::string_view label, std::string_view glyph, std::size_t index);
|
||||
[[nodiscard]] std::unique_ptr<Separator> makeSegmentSeparator();
|
||||
[[nodiscard]] std::unique_ptr<Button> makeSegmentButton(std::string_view label, std::string_view glyph,
|
||||
std::size_t index);
|
||||
void refreshVariants();
|
||||
void applyOuterStyle();
|
||||
[[nodiscard]] float effectiveFontSize() const noexcept;
|
||||
|
||||
std::vector<Separator*> m_separators;
|
||||
std::vector<Button*> m_buttons;
|
||||
std::size_t m_selected = 0;
|
||||
std::function<void(std::size_t)> m_onChange;
|
||||
|
||||
@@ -23,13 +23,54 @@ void Separator::setThickness(float thickness) {
|
||||
markLayoutDirty();
|
||||
}
|
||||
|
||||
void Separator::doLayout(Renderer& /*renderer*/) {
|
||||
bool horizontal = true;
|
||||
if (auto* flex = dynamic_cast<Flex*>(parent()); flex != nullptr) {
|
||||
horizontal = flex->direction() == FlexDirection::Vertical;
|
||||
void Separator::setOrientation(SeparatorOrientation orientation) {
|
||||
if (m_orientation == orientation) {
|
||||
return;
|
||||
}
|
||||
m_orientation = orientation;
|
||||
markLayoutDirty();
|
||||
}
|
||||
|
||||
if (horizontal) {
|
||||
bool Separator::ruleIsHorizontal() const {
|
||||
if (m_orientation == SeparatorOrientation::HorizontalRule) {
|
||||
return true;
|
||||
}
|
||||
if (m_orientation == SeparatorOrientation::VerticalRule) {
|
||||
return false;
|
||||
}
|
||||
if (const auto* flex = dynamic_cast<const Flex*>(parent()); flex != nullptr) {
|
||||
return flex->direction() == FlexDirection::Vertical;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LayoutSize Separator::doMeasure(Renderer& renderer, const LayoutConstraints& constraints) {
|
||||
const bool horiz = ruleIsHorizontal();
|
||||
float w = 0.0f;
|
||||
float h = 0.0f;
|
||||
if (horiz) {
|
||||
h = m_thickness;
|
||||
if (constraints.hasExactWidth()) {
|
||||
w = constraints.maxWidth;
|
||||
} else {
|
||||
w = width() > 0.0f ? width() : m_thickness;
|
||||
}
|
||||
} else {
|
||||
w = m_thickness;
|
||||
if (constraints.hasExactHeight()) {
|
||||
h = constraints.maxHeight;
|
||||
} else {
|
||||
h = height() > 0.0f ? height() : m_thickness;
|
||||
}
|
||||
}
|
||||
setSize(w, h);
|
||||
doLayout(renderer);
|
||||
return constraints.constrain(LayoutSize{w, h});
|
||||
}
|
||||
|
||||
void Separator::doLayout(Renderer& /*renderer*/) {
|
||||
const bool horiz = ruleIsHorizontal();
|
||||
if (horiz) {
|
||||
const float w = width() > 0.0f ? width() : (parent() != nullptr ? parent()->width() : 0.0f);
|
||||
setSize(w, m_thickness);
|
||||
const float halfW = w * 0.5f;
|
||||
@@ -38,28 +79,25 @@ void Separator::doLayout(Renderer& /*renderer*/) {
|
||||
m_rectEnd->setPosition(halfW, 0.0f);
|
||||
m_rectEnd->setFrameSize(w - halfW, m_thickness);
|
||||
} else {
|
||||
const float h = height() > 0.0f ? height() : (parent() != nullptr ? parent()->height() : 0.0f);
|
||||
setSize(m_thickness, h);
|
||||
const float halfH = h * 0.5f;
|
||||
const float lineH = height() > 0.0f ? height() : (parent() != nullptr ? parent()->height() : 0.0f);
|
||||
setSize(m_thickness, lineH);
|
||||
const float halfH = lineH * 0.5f;
|
||||
m_rectStart->setPosition(0.0f, 0.0f);
|
||||
m_rectStart->setFrameSize(m_thickness, halfH);
|
||||
m_rectEnd->setPosition(0.0f, halfH);
|
||||
m_rectEnd->setFrameSize(m_thickness, h - halfH);
|
||||
m_rectEnd->setFrameSize(m_thickness, lineH - halfH);
|
||||
}
|
||||
|
||||
applyPalette();
|
||||
}
|
||||
|
||||
void Separator::applyPalette() {
|
||||
bool horizontal = true;
|
||||
if (auto* flex = dynamic_cast<Flex*>(parent()); flex != nullptr) {
|
||||
horizontal = flex->direction() == FlexDirection::Vertical;
|
||||
}
|
||||
const bool horiz = ruleIsHorizontal();
|
||||
|
||||
const Color opaque = resolveColorSpec(m_color);
|
||||
Color transparent = opaque;
|
||||
transparent.a = 0.0f;
|
||||
const GradientDirection dir = horizontal ? GradientDirection::Horizontal : GradientDirection::Vertical;
|
||||
const GradientDirection dir = horiz ? GradientDirection::Horizontal : GradientDirection::Vertical;
|
||||
|
||||
m_rectStart->setStyle(RoundedRectStyle{
|
||||
.fill = transparent,
|
||||
|
||||
@@ -4,25 +4,38 @@
|
||||
#include "ui/palette.h"
|
||||
#include "ui/signal.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class RectNode;
|
||||
class Renderer;
|
||||
|
||||
enum class SeparatorOrientation : std::uint8_t {
|
||||
// Infer from parent: horizontal rule inside a vertical Flex, vertical rule inside a horizontal Flex.
|
||||
Auto,
|
||||
HorizontalRule,
|
||||
VerticalRule,
|
||||
};
|
||||
|
||||
class Separator : public Node {
|
||||
public:
|
||||
Separator();
|
||||
|
||||
void setColor(const ColorSpec& color);
|
||||
void setThickness(float thickness);
|
||||
void setOrientation(SeparatorOrientation orientation);
|
||||
|
||||
protected:
|
||||
LayoutSize doMeasure(Renderer& renderer, const LayoutConstraints& constraints) override;
|
||||
void doLayout(Renderer& renderer) override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool ruleIsHorizontal() const;
|
||||
void applyPalette();
|
||||
|
||||
RectNode* m_rectStart = nullptr;
|
||||
RectNode* m_rectEnd = nullptr;
|
||||
ColorSpec m_color = colorSpecFromRole(ColorRole::Outline);
|
||||
float m_thickness = 1.0f;
|
||||
SeparatorOrientation m_orientation = SeparatorOrientation::Auto;
|
||||
Signal<>::ScopedConnection m_paletteConn;
|
||||
};
|
||||
|
||||
+88
-25
@@ -1,9 +1,12 @@
|
||||
#include "ui/controls/stepper.h"
|
||||
|
||||
#include "render/core/render_styles.h"
|
||||
#include "render/core/renderer.h"
|
||||
#include "render/scene/input_area.h"
|
||||
#include "ui/controls/button.h"
|
||||
#include "ui/controls/flex.h"
|
||||
#include "ui/controls/input.h"
|
||||
#include "ui/controls/separator.h"
|
||||
#include "ui/palette.h"
|
||||
#include "ui/style.h"
|
||||
|
||||
@@ -17,7 +20,15 @@
|
||||
namespace {
|
||||
|
||||
constexpr float kDefaultMinWidth = 140.0f;
|
||||
constexpr float kValueFieldHPadding = 2.0f;
|
||||
|
||||
std::unique_ptr<Separator> makeStepperSeparator(float scale) {
|
||||
auto sep = std::make_unique<Separator>();
|
||||
sep->setOrientation(SeparatorOrientation::VerticalRule);
|
||||
sep->setThickness(std::max(1.0f, Style::borderWidth * scale));
|
||||
sep->setColor(colorSpecFromRole(ColorRole::Outline, 0.5f));
|
||||
sep->setFlexGrow(0.0f);
|
||||
return sep;
|
||||
}
|
||||
|
||||
std::string trimAscii(std::string_view s) {
|
||||
std::size_t a = 0;
|
||||
@@ -35,24 +46,24 @@ namespace {
|
||||
|
||||
Stepper::Stepper() {
|
||||
setDirection(FlexDirection::Horizontal);
|
||||
setAlign(FlexAlign::Center);
|
||||
setJustify(FlexJustify::SpaceBetween);
|
||||
setAlign(FlexAlign::Stretch);
|
||||
setJustify(FlexJustify::Start);
|
||||
setGap(0.0f);
|
||||
setPadding(Style::spaceXs, Style::spaceXs);
|
||||
setPadding(0.0f);
|
||||
setMinWidth(kDefaultMinWidth);
|
||||
setFill(colorSpecFromRole(ColorRole::SurfaceVariant));
|
||||
setBorder(colorSpecFromRole(ColorRole::Outline), Style::borderWidth);
|
||||
clearBorder();
|
||||
setRadius(Style::radiusMd);
|
||||
|
||||
auto makeStepButton = [this](bool increment) -> std::unique_ptr<Button> {
|
||||
auto btn = std::make_unique<Button>();
|
||||
btn->setVariant(ButtonVariant::Ghost);
|
||||
btn->setVariant(ButtonVariant::Tab);
|
||||
btn->setGlyph(increment ? "plus" : "minus");
|
||||
btn->setGlyphSize(Style::fontSizeBody);
|
||||
btn->setMinWidth(Style::controlHeightSm);
|
||||
btn->setMinHeight(Style::controlHeightSm);
|
||||
btn->setPadding(0.0f);
|
||||
btn->setMinHeight(Style::controlHeight);
|
||||
btn->setPadding(Style::spaceXs, Style::spaceMd);
|
||||
btn->setContentAlign(ButtonContentAlign::Center);
|
||||
btn->setFlexGrow(0.0f);
|
||||
btn->setOnClick([this, increment]() { stepBy(increment ? 1 : -1); });
|
||||
return btn;
|
||||
};
|
||||
@@ -64,19 +75,45 @@ Stepper::Stepper() {
|
||||
}
|
||||
|
||||
{
|
||||
auto sep = makeStepperSeparator(m_scale);
|
||||
m_separatorBeforeValue = sep.get();
|
||||
addChild(std::move(sep));
|
||||
}
|
||||
|
||||
{
|
||||
auto track = std::make_unique<Flex>();
|
||||
track->setDirection(FlexDirection::Horizontal);
|
||||
track->setAlign(FlexAlign::Stretch);
|
||||
track->setJustify(FlexJustify::Center);
|
||||
track->setGap(0.0f);
|
||||
track->setPadding(0.0f);
|
||||
track->setFlexGrow(1.0f);
|
||||
track->setMinHeight(Style::controlHeight);
|
||||
track->clearFill();
|
||||
track->clearBorder();
|
||||
track->setRadii(Radii{});
|
||||
m_valueTrack = track.get();
|
||||
|
||||
auto field = std::make_unique<Input>();
|
||||
field->setFrameVisible(false);
|
||||
field->setBold(true);
|
||||
field->setTextAlign(TextAlign::Center);
|
||||
field->setFontSize(Style::fontSizeBody);
|
||||
field->setControlHeight(Style::controlHeightSm);
|
||||
field->setHorizontalPadding(kValueFieldHPadding);
|
||||
field->setControlHeight(Style::controlHeight);
|
||||
field->setHorizontalPadding(Style::spaceMd);
|
||||
field->setFlexGrow(1.0f);
|
||||
field->setOnSubmit([this](const std::string& /*text*/) { commitValueField(); });
|
||||
field->setOnFocusLoss([this]() { commitValueField(); });
|
||||
field->setOnKeyEvent([this](std::uint32_t sym, std::uint32_t mod) { return swallowNonNumericKey(sym, mod); });
|
||||
m_valueInput = field.get();
|
||||
addChild(std::move(field));
|
||||
track->addChild(std::move(field));
|
||||
addChild(std::move(track));
|
||||
}
|
||||
|
||||
{
|
||||
auto sep = makeStepperSeparator(m_scale);
|
||||
m_separatorAfterValue = sep.get();
|
||||
addChild(std::move(sep));
|
||||
}
|
||||
|
||||
{
|
||||
@@ -87,6 +124,7 @@ Stepper::Stepper() {
|
||||
|
||||
syncValueField();
|
||||
refreshButtons();
|
||||
refreshSegmentStyle();
|
||||
}
|
||||
|
||||
void Stepper::setRange(int minValue, int maxValue) {
|
||||
@@ -144,29 +182,28 @@ void Stepper::setOnValueChanged(std::function<void(int)> callback) { m_onValueCh
|
||||
void Stepper::setScale(float scale) {
|
||||
m_scale = std::max(0.1f, scale);
|
||||
setGap(0.0f);
|
||||
setPadding(Style::spaceXs * m_scale, Style::spaceXs * m_scale);
|
||||
setPadding(0.0f);
|
||||
setMinWidth(kDefaultMinWidth * m_scale);
|
||||
setRadius(Style::radiusMd * m_scale);
|
||||
setBorder(colorSpecFromRole(ColorRole::Outline), Style::borderWidth * m_scale);
|
||||
clearBorder();
|
||||
if (m_valueTrack != nullptr) {
|
||||
m_valueTrack->setMinHeight(Style::controlHeight * m_scale);
|
||||
}
|
||||
if (m_valueInput != nullptr) {
|
||||
m_valueInput->setFontSize(Style::fontSizeBody * m_scale);
|
||||
m_valueInput->setControlHeight(Style::controlHeightSm * m_scale);
|
||||
m_valueInput->setHorizontalPadding(kValueFieldHPadding * m_scale);
|
||||
m_valueInput->setControlHeight(Style::controlHeight * m_scale);
|
||||
m_valueInput->setHorizontalPadding(Style::spaceMd * m_scale);
|
||||
}
|
||||
if (m_decrement != nullptr) {
|
||||
m_decrement->setGlyphSize(Style::fontSizeBody * m_scale);
|
||||
m_decrement->setMinWidth(Style::controlHeightSm * m_scale);
|
||||
m_decrement->setMinHeight(Style::controlHeightSm * m_scale);
|
||||
m_decrement->setPadding(0.0f);
|
||||
m_decrement->setRadius(Style::radiusMd * m_scale);
|
||||
m_decrement->setMinHeight(Style::controlHeight * m_scale);
|
||||
m_decrement->setPadding(Style::spaceXs * m_scale, Style::spaceMd * m_scale);
|
||||
}
|
||||
if (m_increment != nullptr) {
|
||||
m_increment->setGlyphSize(Style::fontSizeBody * m_scale);
|
||||
m_increment->setMinWidth(Style::controlHeightSm * m_scale);
|
||||
m_increment->setMinHeight(Style::controlHeightSm * m_scale);
|
||||
m_increment->setPadding(0.0f);
|
||||
m_increment->setRadius(Style::radiusMd * m_scale);
|
||||
m_increment->setMinHeight(Style::controlHeight * m_scale);
|
||||
m_increment->setPadding(Style::spaceXs * m_scale, Style::spaceMd * m_scale);
|
||||
}
|
||||
refreshSegmentStyle();
|
||||
markLayoutDirty();
|
||||
}
|
||||
|
||||
@@ -274,3 +311,29 @@ void Stepper::refreshButtons() {
|
||||
m_increment->setEnabled(m_enabled && m_value < m_max);
|
||||
}
|
||||
}
|
||||
|
||||
void Stepper::refreshSegmentStyle() {
|
||||
const float r = Style::radiusMd * m_scale;
|
||||
setFill(colorSpecFromRole(ColorRole::SurfaceVariant));
|
||||
clearBorder();
|
||||
setRadius(r);
|
||||
if (m_decrement != nullptr) {
|
||||
m_decrement->setVariant(ButtonVariant::Tab);
|
||||
m_decrement->setRadii({r, 0.0f, 0.0f, r});
|
||||
}
|
||||
if (m_increment != nullptr) {
|
||||
m_increment->setVariant(ButtonVariant::Tab);
|
||||
m_increment->setRadii({0.0f, r, r, 0.0f});
|
||||
}
|
||||
if (m_valueTrack != nullptr) {
|
||||
m_valueTrack->setRadii(Radii{});
|
||||
m_valueTrack->clearFill();
|
||||
}
|
||||
const float ruleW = std::max(1.0f, Style::borderWidth * m_scale);
|
||||
if (m_separatorBeforeValue != nullptr) {
|
||||
m_separatorBeforeValue->setThickness(ruleW);
|
||||
}
|
||||
if (m_separatorAfterValue != nullptr) {
|
||||
m_separatorAfterValue->setThickness(ruleW);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
|
||||
class Button;
|
||||
class Input;
|
||||
class Separator;
|
||||
class Renderer;
|
||||
|
||||
// Horizontal numeric stepper: [ − ] editable value [ + ]
|
||||
// Horizontal numeric stepper styled like Segmented (shared SurfaceVariant track, Tab-style ends + center).
|
||||
class Stepper : public Flex {
|
||||
public:
|
||||
Stepper();
|
||||
@@ -40,8 +41,12 @@ private:
|
||||
void commitValueField();
|
||||
bool swallowNonNumericKey(std::uint32_t sym, std::uint32_t modifiers);
|
||||
void refreshButtons();
|
||||
void refreshSegmentStyle();
|
||||
|
||||
Button* m_decrement = nullptr;
|
||||
Separator* m_separatorBeforeValue = nullptr;
|
||||
Flex* m_valueTrack = nullptr;
|
||||
Separator* m_separatorAfterValue = nullptr;
|
||||
Button* m_increment = nullptr;
|
||||
Input* m_valueInput = nullptr;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user