Label text stages in the chain list with their words

Effect::label(values) names a stage in a list, defaulting to the title;
the text effect answers "Text (words)", and the chain rows are renamed
in place as params change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Si
2026-09-23 21:37:26 +02:00
co-authored by Claude Opus 5.5
parent 134c575727
commit 5292c101db
6 changed files with 47 additions and 2 deletions
+7
View File
@@ -38,6 +38,13 @@ public:
// use. An effect with no kind must declare no styled params.
virtual std::string styleKind() const { return {}; }
// How a stage of this effect is named in a list of stages, given its
// values: the title unless the values say something more telling.
virtual std::string label(const ParamValues& values) const {
(void)values;
return title();
}
virtual Image render(const std::vector<Image>& sources, int width, int height, Rng& rng,
const ParamValues& values) const = 0;
+16
View File
@@ -203,6 +203,22 @@ void TextEffect::loadBoxImage(const std::string& path, Image& image,
alpha = cachedAlpha_;
}
std::string TextEffect::label(const ParamValues& values) const {
// Several lines read as one: each run of whitespace becomes a single space.
std::string words;
bool gap = false;
for (const char c : paramText(values, "text", "")) {
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
gap = !words.empty();
continue;
}
if (gap) words += ' ';
gap = false;
words += c;
}
return words.empty() ? title() : title() + " (" + words + ")";
}
Image TextEffect::render(const std::vector<Image>& sources, int width, int height, Rng& rng,
const ParamValues& values) const {
if (width < 1 || height < 1) throw EffectError("Canvas size must be at least 1x1 pixels.");
+2
View File
@@ -30,6 +30,8 @@ public:
const std::vector<ParamSpec>& params() const override;
std::string styleKind() const override { return "text"; }
bool needsSources() const override { return false; }
// "Text (Hello world)", so stages writing different words tell apart.
std::string label(const ParamValues& values) const override;
Image render(const std::vector<Image>& sources, int width, int height, Rng& rng,
const ParamValues& values) const override;
+12 -2
View File
@@ -1313,6 +1313,7 @@ void EffectorWindow::rebuildChainRows() {
// Only the rows: the placeholder is a child of the list box as well, and
// removing it would leave the empty list blank.
while (auto* row = chainList_.get_row_at_index(0)) chainList_.remove(*row);
stageLabels_.clear();
// The first stage that actually runs is the one handed the sources, which
// is not stage 1 once stage 1 is switched off.
@@ -1343,8 +1344,8 @@ void EffectorWindow::rebuildChainRows() {
});
box->append(*check);
auto* label = Gtk::make_managed<Gtk::Label>(std::to_string(index + 1) + ". " +
stages_[i].effect->title());
auto* label = Gtk::make_managed<Gtk::Label>(stageLabel(i));
stageLabels_.push_back(label);
label->set_xalign(0.0F);
label->set_hexpand(true);
label->set_ellipsize(Pango::EllipsizeMode::END);
@@ -1418,8 +1419,17 @@ void EffectorWindow::onStageSelected() {
// ------------------------------------------------------------ rendering ---
std::string EffectorWindow::stageLabel(std::size_t index) const {
const ChainStage& stage = stages_[index];
return std::to_string(index + 1) + ". " + stage.effect->label(stageValues(stage, styles_));
}
void EffectorWindow::onParamChanged() {
storeParams();
// A param can be what names the stage (the words of a text stage), and a
// styled one can rename every stage sharing the style.
for (std::size_t i = 0; i < stageLabels_.size() && i < stages_.size(); ++i)
stageLabels_[i]->set_text(stageLabel(i));
requestRender();
}
+3
View File
@@ -95,6 +95,7 @@ private:
// The check on a chain row: the stage stays where it is, and is skipped.
void setStageEnabled(int index, bool enabled);
void rebuildChainRows();
std::string stageLabel(std::size_t index) const;
void selectStage(int index);
void onStageSelected();
Gtk::Widget& buildMainMenu(); // the header menu: new, open, save project
@@ -145,6 +146,8 @@ private:
StyleSet styles_;
int selectedStage_ = 0;
bool suspendStageSelect_ = false; // guards the rebuild of the chain rows
// The name label of each chain row, renamed in place as params change.
std::vector<Gtk::Label*> stageLabels_;
Image result_;
std::uint64_t seed_ = 0;
std::string projectPath_; // empty until the project has been saved once
+7
View File
@@ -857,6 +857,13 @@ int main() {
// Like blur and overlay it needs no sources of its own, and the same
// settings render the same bytes twice over.
check(!TextEffect().needsSources(), "text can lead a chain");
// A text stage is named after its words, on one line.
check(TextEffect().label({{"text", std::string(" Two\nlines ")}}) == "Text (Two lines)",
"a text stage is labelled with its words");
check(TextEffect().label({{"text", std::string(" \n")}}) == "Text",
"a text stage with no words is labelled with the title alone");
check(effectByName("blur")->label({}) == "Blur", "other effects are labelled with their title");
const auto alone = renderText({}, 200, 120, plain);
check(alone.width() == 200 && countRed(alone, 0, 0, 200, 120) > 0,
"without sources it draws on an empty canvas");