mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
feat: factorized list_editor control and make it slightly tighter
This commit is contained in:
@@ -547,6 +547,7 @@ _noctalia_sources = files(
|
||||
'src/ui/controls/image.cpp',
|
||||
'src/ui/controls/input.cpp',
|
||||
'src/ui/controls/label.cpp',
|
||||
'src/ui/controls/list_editor.cpp',
|
||||
'src/ui/controls/progress_bar.cpp',
|
||||
'src/ui/controls/radio_button.cpp',
|
||||
'src/ui/controls/scroll_view.cpp',
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "ui/controls/glyph.h"
|
||||
#include "ui/controls/input.h"
|
||||
#include "ui/controls/label.h"
|
||||
#include "ui/controls/list_editor.h"
|
||||
#include "ui/controls/search_picker.h"
|
||||
#include "ui/controls/segmented.h"
|
||||
#include "ui/controls/select.h"
|
||||
@@ -24,8 +25,10 @@
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <format>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <locale>
|
||||
#include <memory>
|
||||
@@ -1035,194 +1038,48 @@ namespace settings {
|
||||
colorSpecFromRole(ColorRole::OnSurfaceVariant), false));
|
||||
}
|
||||
|
||||
const auto resolveItemLabel = [&list](const std::string& value) -> std::string {
|
||||
for (const auto& opt : list.suggestedOptions) {
|
||||
if (opt.value == value) {
|
||||
return opt.label;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const float labelCellWidth = 200.0f * scale;
|
||||
for (std::size_t i = 0; i < list.items.size(); ++i) {
|
||||
auto itemRow = std::make_unique<Flex>();
|
||||
itemRow->setDirection(FlexDirection::Horizontal);
|
||||
itemRow->setAlign(FlexAlign::Center);
|
||||
itemRow->setGap(Style::spaceXs * scale);
|
||||
itemRow->setMinHeight(Style::controlHeightSm * scale);
|
||||
|
||||
auto labelCell = std::make_unique<Flex>();
|
||||
labelCell->setDirection(FlexDirection::Horizontal);
|
||||
labelCell->setAlign(FlexAlign::Center);
|
||||
labelCell->setMinWidth(labelCellWidth);
|
||||
labelCell->addChild(makeLabel(resolveItemLabel(list.items[i]), Style::fontSizeCaption * scale,
|
||||
colorSpecFromRole(ColorRole::OnSurface), false));
|
||||
itemRow->addChild(std::move(labelCell));
|
||||
|
||||
auto removeBtn = std::make_unique<Button>();
|
||||
removeBtn->setGlyph("close");
|
||||
removeBtn->setVariant(ButtonVariant::Ghost);
|
||||
removeBtn->setGlyphSize(Style::fontSizeCaption * scale);
|
||||
removeBtn->setMinWidth(Style::controlHeightSm * scale);
|
||||
removeBtn->setMinHeight(Style::controlHeightSm * scale);
|
||||
removeBtn->setPadding(Style::spaceXs * scale);
|
||||
removeBtn->setRadius(Style::radiusSm * scale);
|
||||
{
|
||||
auto items = list.items;
|
||||
auto path = entry.path;
|
||||
const Config& config = cfg;
|
||||
removeBtn->setOnClick(
|
||||
[setOverride = ctx.setOverride, setOverrides = ctx.setOverrides, &config, items, path, i]() mutable {
|
||||
const std::string removedItem = items[i];
|
||||
items.erase(items.begin() + static_cast<std::ptrdiff_t>(i));
|
||||
const auto overrides = capsuleGroupRemovalOverrides(config, path, removedItem, items);
|
||||
if (overrides.size() == 1) {
|
||||
setOverride(path, items);
|
||||
return;
|
||||
}
|
||||
setOverrides(overrides);
|
||||
});
|
||||
}
|
||||
itemRow->addChild(std::move(removeBtn));
|
||||
|
||||
if (i > 0) {
|
||||
auto upBtn = std::make_unique<Button>();
|
||||
upBtn->setGlyph("chevron-up");
|
||||
upBtn->setVariant(ButtonVariant::Ghost);
|
||||
upBtn->setGlyphSize(Style::fontSizeCaption * scale);
|
||||
upBtn->setMinWidth(Style::controlHeightSm * scale);
|
||||
upBtn->setMinHeight(Style::controlHeightSm * scale);
|
||||
upBtn->setPadding(Style::spaceXs * scale);
|
||||
upBtn->setRadius(Style::radiusSm * scale);
|
||||
auto items = list.items;
|
||||
auto path = entry.path;
|
||||
upBtn->setOnClick([setOverride = ctx.setOverride, items, path, i]() mutable {
|
||||
std::swap(items[i], items[i - 1]);
|
||||
auto listEditor = std::make_unique<ListEditor>();
|
||||
listEditor->setScale(scale);
|
||||
listEditor->setAddPlaceholder(i18n::tr("settings.controls.list.add-entry-placeholder"));
|
||||
std::vector<ListEditorOption> suggestedOptions;
|
||||
suggestedOptions.reserve(list.suggestedOptions.size());
|
||||
for (const auto& opt : list.suggestedOptions) {
|
||||
suggestedOptions.push_back(ListEditorOption{.value = opt.value, .label = opt.label});
|
||||
}
|
||||
listEditor->setSuggestedOptions(std::move(suggestedOptions));
|
||||
listEditor->setItems(list.items);
|
||||
listEditor->setOnAddRequested(
|
||||
[setOverride = ctx.setOverride, items = list.items, path = entry.path](std::string value) mutable {
|
||||
if (value.empty()) {
|
||||
return;
|
||||
}
|
||||
items.push_back(std::move(value));
|
||||
setOverride(path, items);
|
||||
});
|
||||
itemRow->addChild(std::move(upBtn));
|
||||
}
|
||||
if (i + 1 < list.items.size()) {
|
||||
auto downBtn = std::make_unique<Button>();
|
||||
downBtn->setGlyph("chevron-down");
|
||||
downBtn->setVariant(ButtonVariant::Ghost);
|
||||
downBtn->setGlyphSize(Style::fontSizeCaption * scale);
|
||||
downBtn->setMinWidth(Style::controlHeightSm * scale);
|
||||
downBtn->setMinHeight(Style::controlHeightSm * scale);
|
||||
downBtn->setPadding(Style::spaceXs * scale);
|
||||
downBtn->setRadius(Style::radiusSm * scale);
|
||||
auto items = list.items;
|
||||
auto path = entry.path;
|
||||
downBtn->setOnClick([setOverride = ctx.setOverride, items, path, i]() mutable {
|
||||
std::swap(items[i], items[i + 1]);
|
||||
setOverride(path, items);
|
||||
});
|
||||
itemRow->addChild(std::move(downBtn));
|
||||
}
|
||||
|
||||
block->addChild(std::move(itemRow));
|
||||
}
|
||||
|
||||
auto addRow = std::make_unique<Flex>();
|
||||
addRow->setDirection(FlexDirection::Horizontal);
|
||||
addRow->setAlign(FlexAlign::Center);
|
||||
addRow->setGap(Style::spaceSm * scale);
|
||||
|
||||
const bool useSelectAdder = !list.suggestedOptions.empty();
|
||||
std::vector<SelectOption> remaining;
|
||||
if (useSelectAdder) {
|
||||
remaining.reserve(list.suggestedOptions.size());
|
||||
for (const auto& opt : list.suggestedOptions) {
|
||||
if (std::find(list.items.begin(), list.items.end(), opt.value) == list.items.end()) {
|
||||
remaining.push_back(opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (useSelectAdder) {
|
||||
if (remaining.empty()) {
|
||||
// Every suggested value is already in the list — nothing to add.
|
||||
section.addChild(std::move(block));
|
||||
listEditor->setOnRemoveRequested([setOverride = ctx.setOverride, setOverrides = ctx.setOverrides,
|
||||
config = std::cref(cfg), items = list.items,
|
||||
path = entry.path](std::size_t index) mutable {
|
||||
if (index >= items.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> remainingLabels;
|
||||
remainingLabels.reserve(remaining.size());
|
||||
for (const auto& opt : remaining) {
|
||||
remainingLabels.push_back(opt.label);
|
||||
}
|
||||
|
||||
auto select = std::make_unique<Select>();
|
||||
select->setOptions(remainingLabels);
|
||||
select->setPlaceholder(i18n::tr("settings.controls.list.add-entry-placeholder"));
|
||||
select->setFontSize(Style::fontSizeCaption * scale);
|
||||
select->setControlHeight(Style::controlHeightSm * scale);
|
||||
select->setGlyphSize(Style::fontSizeCaption * scale);
|
||||
select->setSize(labelCellWidth, Style::controlHeightSm * scale);
|
||||
auto* selectPtr = select.get();
|
||||
|
||||
auto addBtn = std::make_unique<Button>();
|
||||
addBtn->setGlyph("add");
|
||||
addBtn->setVariant(ButtonVariant::Ghost);
|
||||
addBtn->setGlyphSize(Style::fontSizeCaption * scale);
|
||||
addBtn->setMinWidth(Style::controlHeightSm * scale);
|
||||
addBtn->setMinHeight(Style::controlHeightSm * scale);
|
||||
addBtn->setPadding(Style::spaceXs * scale);
|
||||
addBtn->setRadius(Style::radiusSm * scale);
|
||||
|
||||
auto items = list.items;
|
||||
auto path = entry.path;
|
||||
addBtn->setOnClick([setOverride = ctx.setOverride, selectPtr, remaining, items, path]() mutable {
|
||||
const std::size_t index = selectPtr->selectedIndex();
|
||||
if (index >= remaining.size()) {
|
||||
return;
|
||||
}
|
||||
items.push_back(remaining[index].value);
|
||||
const std::string removedItem = items[index];
|
||||
items.erase(items.begin() + static_cast<std::ptrdiff_t>(index));
|
||||
const auto overrides = capsuleGroupRemovalOverrides(config.get(), path, removedItem, items);
|
||||
if (overrides.size() == 1) {
|
||||
setOverride(path, items);
|
||||
});
|
||||
|
||||
addRow->addChild(std::move(select));
|
||||
addRow->addChild(std::move(addBtn));
|
||||
} else {
|
||||
auto addInput = std::make_unique<Input>();
|
||||
addInput->setPlaceholder(i18n::tr("settings.controls.list.add-entry-placeholder"));
|
||||
addInput->setFontSize(Style::fontSizeBody * scale);
|
||||
addInput->setControlHeight(Style::controlHeight * scale);
|
||||
addInput->setHorizontalPadding(Style::spaceSm * scale);
|
||||
addInput->setSize(190.0f * scale, Style::controlHeight * scale);
|
||||
auto* addInputPtr = addInput.get();
|
||||
|
||||
auto addBtn = std::make_unique<Button>();
|
||||
addBtn->setGlyph("add");
|
||||
addBtn->setVariant(ButtonVariant::Ghost);
|
||||
addBtn->setGlyphSize(Style::fontSizeBody * scale);
|
||||
addBtn->setMinWidth(Style::controlHeight * scale);
|
||||
addBtn->setMinHeight(Style::controlHeight * scale);
|
||||
addBtn->setPadding(Style::spaceSm * scale);
|
||||
addBtn->setRadius(Style::radiusMd * scale);
|
||||
auto items = list.items;
|
||||
auto path = entry.path;
|
||||
addBtn->setOnClick([setOverride = ctx.setOverride, addInputPtr, items, path]() mutable {
|
||||
const auto& text = addInputPtr->value();
|
||||
if (!text.empty()) {
|
||||
items.push_back(text);
|
||||
setOverride(path, items);
|
||||
}
|
||||
});
|
||||
|
||||
addInput->setOnSubmit([setOverride = ctx.setOverride, items, path](const std::string& text) mutable {
|
||||
if (!text.empty()) {
|
||||
items.push_back(text);
|
||||
setOverride(path, items);
|
||||
}
|
||||
});
|
||||
|
||||
addRow->addChild(std::move(addInput));
|
||||
addRow->addChild(std::move(addBtn));
|
||||
}
|
||||
|
||||
block->addChild(std::move(addRow));
|
||||
return;
|
||||
}
|
||||
setOverrides(overrides);
|
||||
});
|
||||
listEditor->setOnMoveRequested([setOverride = ctx.setOverride, items = list.items,
|
||||
path = entry.path](std::size_t from, std::size_t to) mutable {
|
||||
if (from >= items.size() || to >= items.size() || from == to) {
|
||||
return;
|
||||
}
|
||||
std::swap(items[from], items[to]);
|
||||
setOverride(path, items);
|
||||
});
|
||||
block->addChild(std::move(listEditor));
|
||||
|
||||
section.addChild(std::move(block));
|
||||
};
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
#include "ui/controls/list_editor.h"
|
||||
|
||||
#include "ui/controls/button.h"
|
||||
#include "ui/controls/input.h"
|
||||
#include "ui/controls/label.h"
|
||||
#include "ui/controls/select.h"
|
||||
#include "ui/palette.h"
|
||||
#include "ui/style.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr float kLabelCellWidth = 200.0f;
|
||||
constexpr float kFreeformInputWidth = 190.0f;
|
||||
constexpr float kItemRowHeight = 26.0f;
|
||||
constexpr float kSuggestedAddHeight = 30.0f;
|
||||
constexpr float kVerticalGap = 2.0f;
|
||||
|
||||
std::unique_ptr<Label> makeListLabel(std::string_view text, float scale) {
|
||||
auto label = std::make_unique<Label>();
|
||||
label->setText(text);
|
||||
label->setFontSize(Style::fontSizeCaption * scale);
|
||||
label->setColor(colorSpecFromRole(ColorRole::OnSurface));
|
||||
return label;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ListEditor::ListEditor() {
|
||||
setDirection(FlexDirection::Vertical);
|
||||
setAlign(FlexAlign::Stretch);
|
||||
setGap(kVerticalGap);
|
||||
}
|
||||
|
||||
void ListEditor::setItems(std::vector<std::string> items) {
|
||||
m_items = std::move(items);
|
||||
rebuildRows();
|
||||
}
|
||||
|
||||
void ListEditor::setSuggestedOptions(std::vector<ListEditorOption> options) {
|
||||
m_suggestedOptions = std::move(options);
|
||||
rebuildRows();
|
||||
}
|
||||
|
||||
void ListEditor::setAddPlaceholder(std::string_view placeholder) {
|
||||
m_addPlaceholder = std::string(placeholder);
|
||||
rebuildRows();
|
||||
}
|
||||
|
||||
void ListEditor::setScale(float scale) {
|
||||
m_scale = std::max(0.1f, scale);
|
||||
setGap(kVerticalGap * m_scale);
|
||||
rebuildRows();
|
||||
}
|
||||
|
||||
void ListEditor::setOnAddRequested(std::function<void(std::string)> callback) {
|
||||
m_onAddRequested = std::move(callback);
|
||||
}
|
||||
|
||||
void ListEditor::setOnRemoveRequested(std::function<void(std::size_t)> callback) {
|
||||
m_onRemoveRequested = std::move(callback);
|
||||
}
|
||||
|
||||
void ListEditor::setOnMoveRequested(std::function<void(std::size_t, std::size_t)> callback) {
|
||||
m_onMoveRequested = std::move(callback);
|
||||
}
|
||||
|
||||
std::string ListEditor::labelForValue(std::string_view value) const {
|
||||
for (const auto& opt : m_suggestedOptions) {
|
||||
if (opt.value == value) {
|
||||
return opt.label;
|
||||
}
|
||||
}
|
||||
return std::string(value);
|
||||
}
|
||||
|
||||
std::vector<ListEditorOption> ListEditor::remainingOptions() const {
|
||||
std::vector<ListEditorOption> remaining;
|
||||
remaining.reserve(m_suggestedOptions.size());
|
||||
for (const auto& opt : m_suggestedOptions) {
|
||||
if (std::find(m_items.begin(), m_items.end(), opt.value) == m_items.end()) {
|
||||
remaining.push_back(opt);
|
||||
}
|
||||
}
|
||||
return remaining;
|
||||
}
|
||||
|
||||
void ListEditor::rebuildRows() {
|
||||
while (!children().empty()) {
|
||||
removeChild(children().back().get());
|
||||
}
|
||||
|
||||
const float labelCellWidth = kLabelCellWidth * m_scale;
|
||||
const float itemRowHeight = kItemRowHeight * m_scale;
|
||||
const float suggestedAddHeight = kSuggestedAddHeight * m_scale;
|
||||
|
||||
for (std::size_t i = 0; i < m_items.size(); ++i) {
|
||||
auto itemRow = std::make_unique<Flex>();
|
||||
itemRow->setDirection(FlexDirection::Horizontal);
|
||||
itemRow->setAlign(FlexAlign::Center);
|
||||
itemRow->setGap(Style::spaceXs * m_scale);
|
||||
itemRow->setMinHeight(itemRowHeight);
|
||||
|
||||
auto labelCell = std::make_unique<Flex>();
|
||||
labelCell->setDirection(FlexDirection::Horizontal);
|
||||
labelCell->setAlign(FlexAlign::Center);
|
||||
labelCell->setMinWidth(labelCellWidth);
|
||||
labelCell->addChild(makeListLabel(labelForValue(m_items[i]), m_scale));
|
||||
itemRow->addChild(std::move(labelCell));
|
||||
|
||||
addGhostIconButton(*itemRow, "close", Style::fontSizeCaption * m_scale, [this, i] {
|
||||
if (m_onRemoveRequested) {
|
||||
m_onRemoveRequested(i);
|
||||
}
|
||||
});
|
||||
|
||||
if (i > 0) {
|
||||
addGhostIconButton(*itemRow, "chevron-up", Style::fontSizeCaption * m_scale, [this, i] {
|
||||
if (m_onMoveRequested) {
|
||||
m_onMoveRequested(i, i - 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (i + 1 < m_items.size()) {
|
||||
addGhostIconButton(*itemRow, "chevron-down", Style::fontSizeCaption * m_scale, [this, i] {
|
||||
if (m_onMoveRequested) {
|
||||
m_onMoveRequested(i, i + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addChild(std::move(itemRow));
|
||||
}
|
||||
|
||||
auto addRow = std::make_unique<Flex>();
|
||||
addRow->setDirection(FlexDirection::Horizontal);
|
||||
addRow->setAlign(FlexAlign::Center);
|
||||
addRow->setGap(Style::spaceSm * m_scale);
|
||||
|
||||
if (!m_suggestedOptions.empty()) {
|
||||
const auto remaining = remainingOptions();
|
||||
if (remaining.empty()) {
|
||||
markLayoutDirty();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> remainingLabels;
|
||||
remainingLabels.reserve(remaining.size());
|
||||
for (const auto& opt : remaining) {
|
||||
remainingLabels.push_back(opt.label);
|
||||
}
|
||||
|
||||
auto select = std::make_unique<Select>();
|
||||
select->setOptions(std::move(remainingLabels));
|
||||
select->setPlaceholder(m_addPlaceholder);
|
||||
select->setFontSize(Style::fontSizeCaption * m_scale);
|
||||
select->setControlHeight(suggestedAddHeight);
|
||||
select->setGlyphSize(Style::fontSizeCaption * m_scale);
|
||||
select->setSize(labelCellWidth, suggestedAddHeight);
|
||||
auto* selectPtr = select.get();
|
||||
|
||||
auto addBtn = std::make_unique<Button>();
|
||||
addBtn->setGlyph("add");
|
||||
addBtn->setVariant(ButtonVariant::Ghost);
|
||||
addBtn->setGlyphSize(Style::fontSizeCaption * m_scale);
|
||||
addBtn->setMinWidth(suggestedAddHeight);
|
||||
addBtn->setMinHeight(suggestedAddHeight);
|
||||
addBtn->setPadding(Style::spaceXs * m_scale);
|
||||
addBtn->setRadius(Style::radiusSm * m_scale);
|
||||
addBtn->setOnClick([this, selectPtr, remaining] {
|
||||
const std::size_t index = selectPtr->selectedIndex();
|
||||
if (index < remaining.size() && m_onAddRequested) {
|
||||
m_onAddRequested(remaining[index].value);
|
||||
}
|
||||
});
|
||||
|
||||
addRow->addChild(std::move(select));
|
||||
addRow->addChild(std::move(addBtn));
|
||||
} else {
|
||||
auto addInput = std::make_unique<Input>();
|
||||
addInput->setPlaceholder(m_addPlaceholder);
|
||||
addInput->setFontSize(Style::fontSizeBody * m_scale);
|
||||
addInput->setControlHeight(Style::controlHeight * m_scale);
|
||||
addInput->setHorizontalPadding(Style::spaceSm * m_scale);
|
||||
addInput->setSize(kFreeformInputWidth * m_scale, Style::controlHeight * m_scale);
|
||||
auto* addInputPtr = addInput.get();
|
||||
|
||||
auto addBtn = std::make_unique<Button>();
|
||||
addBtn->setGlyph("add");
|
||||
addBtn->setVariant(ButtonVariant::Ghost);
|
||||
addBtn->setGlyphSize(Style::fontSizeBody * m_scale);
|
||||
addBtn->setMinWidth(Style::controlHeight * m_scale);
|
||||
addBtn->setMinHeight(Style::controlHeight * m_scale);
|
||||
addBtn->setPadding(Style::spaceSm * m_scale);
|
||||
addBtn->setRadius(Style::radiusMd * m_scale);
|
||||
addBtn->setOnClick([this, addInputPtr] {
|
||||
const auto& text = addInputPtr->value();
|
||||
if (!text.empty() && m_onAddRequested) {
|
||||
m_onAddRequested(text);
|
||||
}
|
||||
});
|
||||
|
||||
addInput->setOnSubmit([this](const std::string& text) {
|
||||
if (!text.empty() && m_onAddRequested) {
|
||||
m_onAddRequested(text);
|
||||
}
|
||||
});
|
||||
|
||||
addRow->addChild(std::move(addInput));
|
||||
addRow->addChild(std::move(addBtn));
|
||||
}
|
||||
|
||||
addChild(std::move(addRow));
|
||||
markLayoutDirty();
|
||||
}
|
||||
|
||||
void ListEditor::addGhostIconButton(Flex& row, std::string_view glyph, float size, std::function<void()> callback) {
|
||||
auto button = std::make_unique<Button>();
|
||||
button->setGlyph(glyph);
|
||||
button->setVariant(ButtonVariant::Ghost);
|
||||
button->setGlyphSize(size);
|
||||
button->setMinWidth(kItemRowHeight * m_scale);
|
||||
button->setMinHeight(kItemRowHeight * m_scale);
|
||||
button->setPadding(Style::spaceXs * m_scale);
|
||||
button->setRadius(Style::radiusSm * m_scale);
|
||||
button->setOnClick(std::move(callback));
|
||||
row.addChild(std::move(button));
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/controls/flex.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
struct ListEditorOption {
|
||||
std::string value;
|
||||
std::string label;
|
||||
};
|
||||
|
||||
class ListEditor : public Flex {
|
||||
public:
|
||||
ListEditor();
|
||||
|
||||
void setItems(std::vector<std::string> items);
|
||||
void setSuggestedOptions(std::vector<ListEditorOption> options);
|
||||
void setAddPlaceholder(std::string_view placeholder);
|
||||
void setScale(float scale);
|
||||
void setOnAddRequested(std::function<void(std::string)> callback);
|
||||
void setOnRemoveRequested(std::function<void(std::size_t)> callback);
|
||||
void setOnMoveRequested(std::function<void(std::size_t, std::size_t)> callback);
|
||||
|
||||
[[nodiscard]] const std::vector<std::string>& items() const noexcept { return m_items; }
|
||||
[[nodiscard]] const std::vector<ListEditorOption>& suggestedOptions() const noexcept { return m_suggestedOptions; }
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::string labelForValue(std::string_view value) const;
|
||||
[[nodiscard]] std::vector<ListEditorOption> remainingOptions() const;
|
||||
void rebuildRows();
|
||||
void addGhostIconButton(Flex& row, std::string_view glyph, float size, std::function<void()> callback);
|
||||
|
||||
std::vector<std::string> m_items;
|
||||
std::vector<ListEditorOption> m_suggestedOptions;
|
||||
std::string m_addPlaceholder;
|
||||
std::function<void(std::string)> m_onAddRequested;
|
||||
std::function<void(std::size_t)> m_onRemoveRequested;
|
||||
std::function<void(std::size_t, std::size_t)> m_onMoveRequested;
|
||||
float m_scale = 1.0f;
|
||||
};
|
||||
Reference in New Issue
Block a user