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:
@@ -38,6 +38,13 @@ public:
|
|||||||
// use. An effect with no kind must declare no styled params.
|
// use. An effect with no kind must declare no styled params.
|
||||||
virtual std::string styleKind() const { return {}; }
|
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,
|
virtual Image render(const std::vector<Image>& sources, int width, int height, Rng& rng,
|
||||||
const ParamValues& values) const = 0;
|
const ParamValues& values) const = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -203,6 +203,22 @@ void TextEffect::loadBoxImage(const std::string& path, Image& image,
|
|||||||
alpha = cachedAlpha_;
|
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,
|
Image TextEffect::render(const std::vector<Image>& sources, int width, int height, Rng& rng,
|
||||||
const ParamValues& values) const {
|
const ParamValues& values) const {
|
||||||
if (width < 1 || height < 1) throw EffectError("Canvas size must be at least 1x1 pixels.");
|
if (width < 1 || height < 1) throw EffectError("Canvas size must be at least 1x1 pixels.");
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ public:
|
|||||||
const std::vector<ParamSpec>& params() const override;
|
const std::vector<ParamSpec>& params() const override;
|
||||||
std::string styleKind() const override { return "text"; }
|
std::string styleKind() const override { return "text"; }
|
||||||
bool needsSources() const override { return false; }
|
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,
|
Image render(const std::vector<Image>& sources, int width, int height, Rng& rng,
|
||||||
const ParamValues& values) const override;
|
const ParamValues& values) const override;
|
||||||
|
|||||||
+12
-2
@@ -1313,6 +1313,7 @@ void EffectorWindow::rebuildChainRows() {
|
|||||||
// Only the rows: the placeholder is a child of the list box as well, and
|
// Only the rows: the placeholder is a child of the list box as well, and
|
||||||
// removing it would leave the empty list blank.
|
// removing it would leave the empty list blank.
|
||||||
while (auto* row = chainList_.get_row_at_index(0)) chainList_.remove(*row);
|
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
|
// The first stage that actually runs is the one handed the sources, which
|
||||||
// is not stage 1 once stage 1 is switched off.
|
// is not stage 1 once stage 1 is switched off.
|
||||||
@@ -1343,8 +1344,8 @@ void EffectorWindow::rebuildChainRows() {
|
|||||||
});
|
});
|
||||||
box->append(*check);
|
box->append(*check);
|
||||||
|
|
||||||
auto* label = Gtk::make_managed<Gtk::Label>(std::to_string(index + 1) + ". " +
|
auto* label = Gtk::make_managed<Gtk::Label>(stageLabel(i));
|
||||||
stages_[i].effect->title());
|
stageLabels_.push_back(label);
|
||||||
label->set_xalign(0.0F);
|
label->set_xalign(0.0F);
|
||||||
label->set_hexpand(true);
|
label->set_hexpand(true);
|
||||||
label->set_ellipsize(Pango::EllipsizeMode::END);
|
label->set_ellipsize(Pango::EllipsizeMode::END);
|
||||||
@@ -1418,8 +1419,17 @@ void EffectorWindow::onStageSelected() {
|
|||||||
|
|
||||||
// ------------------------------------------------------------ rendering ---
|
// ------------------------------------------------------------ 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() {
|
void EffectorWindow::onParamChanged() {
|
||||||
storeParams();
|
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();
|
requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ private:
|
|||||||
// The check on a chain row: the stage stays where it is, and is skipped.
|
// The check on a chain row: the stage stays where it is, and is skipped.
|
||||||
void setStageEnabled(int index, bool enabled);
|
void setStageEnabled(int index, bool enabled);
|
||||||
void rebuildChainRows();
|
void rebuildChainRows();
|
||||||
|
std::string stageLabel(std::size_t index) const;
|
||||||
void selectStage(int index);
|
void selectStage(int index);
|
||||||
void onStageSelected();
|
void onStageSelected();
|
||||||
Gtk::Widget& buildMainMenu(); // the header menu: new, open, save project
|
Gtk::Widget& buildMainMenu(); // the header menu: new, open, save project
|
||||||
@@ -145,6 +146,8 @@ private:
|
|||||||
StyleSet styles_;
|
StyleSet styles_;
|
||||||
int selectedStage_ = 0;
|
int selectedStage_ = 0;
|
||||||
bool suspendStageSelect_ = false; // guards the rebuild of the chain rows
|
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_;
|
Image result_;
|
||||||
std::uint64_t seed_ = 0;
|
std::uint64_t seed_ = 0;
|
||||||
std::string projectPath_; // empty until the project has been saved once
|
std::string projectPath_; // empty until the project has been saved once
|
||||||
|
|||||||
@@ -857,6 +857,13 @@ int main() {
|
|||||||
// Like blur and overlay it needs no sources of its own, and the same
|
// Like blur and overlay it needs no sources of its own, and the same
|
||||||
// settings render the same bytes twice over.
|
// settings render the same bytes twice over.
|
||||||
check(!TextEffect().needsSources(), "text can lead a chain");
|
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);
|
const auto alone = renderText({}, 200, 120, plain);
|
||||||
check(alone.width() == 200 && countRed(alone, 0, 0, 200, 120) > 0,
|
check(alone.width() == 200 && countRed(alone, 0, 0, 200, 120) > 0,
|
||||||
"without sources it draws on an empty canvas");
|
"without sources it draws on an empty canvas");
|
||||||
|
|||||||
Reference in New Issue
Block a user