diff --git a/src/gui/window.cpp b/src/gui/window.cpp index 0c01216..5c20daf 100644 --- a/src/gui/window.cpp +++ b/src/gui/window.cpp @@ -85,13 +85,6 @@ void EffectorWindow::buildUi() { menuButton_.set_popover(static_cast(buildMainMenu())); header->pack_end(menuButton_); - auto* addButton = Gtk::make_managed(); - addButton->set_icon_name("list-add-symbolic"); - addButton->set_tooltip_text("Add images"); - addButton->signal_clicked().connect(sigc::mem_fun(*this, &EffectorWindow::onAddImages)); - header->pack_start(*addButton); - addButton_ = addButton; - saveButton_.set_sensitive(false); saveButton_.signal_clicked().connect(sigc::mem_fun(*this, &EffectorWindow::onSave)); header->pack_end(saveButton_); @@ -120,24 +113,64 @@ void EffectorWindow::buildUi() { root->append(*paned); } +// The chain is what the interface is about, so it comes first and the params +// of the selected stage sit right below it. The sources are a prerequisite, +// not the subject: they live at the bottom in a collapsed expander. Gtk::Widget& EffectorWindow::buildSidebar() { auto* sidebar = Gtk::make_managed(Gtk::Orientation::VERTICAL, 12); sidebar->set_margin(12); - sidebar->append(*sectionLabel("Source images")); + sidebar->append(buildChainSection()); + + paramHeading_.set_xalign(0.0F); + paramHeading_.add_css_class("heading"); + sidebar->append(paramHeading_); + paramGrid_.set_column_spacing(8); + paramGrid_.set_row_spacing(6); + auto* paramScroller = Gtk::make_managed(); + paramScroller->set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC); + paramScroller->set_child(paramGrid_); + paramScroller->set_vexpand(true); + sidebar->append(*paramScroller); + rebuildChainRows(); + buildParamWidgets(); + + sidebar->append(*Gtk::make_managed(Gtk::Orientation::HORIZONTAL)); + sourceList_.set_selection_mode(Gtk::SelectionMode::NONE); sourceList_.add_css_class("boxed-list"); auto* empty = Gtk::make_managed("No images yet"); - empty->set_margin_top(18); - empty->set_margin_bottom(18); + empty->set_margin_top(12); + empty->set_margin_bottom(12); sourceList_.set_placeholder(*empty); auto* scroller = Gtk::make_managed(); scroller->set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC); scroller->set_child(sourceList_); - scroller->set_size_request(-1, 170); - scroller->set_vexpand(true); - sidebar->append(*scroller); + scroller->set_size_request(-1, 140); + scroller->set_margin_top(6); + + auto* sourcesBox = Gtk::make_managed(Gtk::Orientation::VERTICAL, 6); + sourcesBox->append(*scroller); + + // Same shape as "Add effect": the icon and the text are put together by + // hand because set_label() and set_icon_name() replace one another. + auto* addBox = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 6); + addBox->append(*Gtk::make_managed( + Gio::ThemedIcon::create("list-add-symbolic"))); + addBox->append(*Gtk::make_managed("Add images")); + auto* addButton = Gtk::make_managed(); + addButton->set_child(*addBox); + addButton->set_halign(Gtk::Align::START); + addButton->set_tooltip_text("Add source images"); + addButton->signal_clicked().connect(sigc::mem_fun(*this, &EffectorWindow::onAddImages)); + sourcesBox->append(*addButton); + addButton_ = addButton; + + sourcesExpander_.set_child(*sourcesBox); + sourcesExpander_.set_expanded(false); + updateSourcesLabel(); + sidebar->append(sourcesExpander_); sidebar->append(*sectionLabel("Canvas")); auto* grid = Gtk::make_managed(); @@ -163,17 +196,6 @@ Gtk::Widget& EffectorWindow::buildSidebar() { grid->attach(heightSpin_, 1, 1); sidebar->append(*grid); - sidebar->append(buildChainSection()); - - paramHeading_.set_xalign(0.0F); - paramHeading_.add_css_class("heading"); - sidebar->append(paramHeading_); - paramGrid_.set_column_spacing(8); - paramGrid_.set_row_spacing(6); - sidebar->append(paramGrid_); - rebuildChainRows(); - buildParamWidgets(); - auto* seedBox = Gtk::make_managed(Gtk::Orientation::HORIZONTAL, 8); auto* seedLabel = Gtk::make_managed("Seed"); seedLabel->set_xalign(0.0F); @@ -187,6 +209,15 @@ Gtk::Widget& EffectorWindow::buildSidebar() { return *sidebar; } +// The expander is collapsed by default, so its label carries the count that +// used to be plain to see in the list. +void EffectorWindow::updateSourcesLabel() { + int count = 0; + while (sourceList_.get_row_at_index(count) != nullptr) ++count; + sourcesExpander_.set_label(count == 0 ? "Source images" + : "Source images (" + std::to_string(count) + ")"); +} + Gtk::Widget& EffectorWindow::buildPreview() { picture_.set_can_shrink(true); picture_.set_content_fit(Gtk::ContentFit::CONTAIN); @@ -565,6 +596,7 @@ void EffectorWindow::appendSourceRow(const std::string& path) { row->set_child(*box); sourceList_.append(*row); + updateSourcesLabel(); } void EffectorWindow::removeSource(Gtk::ListBoxRow* row) { @@ -575,6 +607,7 @@ void EffectorWindow::removeSource(Gtk::ListBoxRow* row) { } }); sourceList_.remove(*row); + updateSourcesLabel(); requestRender(); } @@ -622,7 +655,12 @@ Gtk::Widget& EffectorWindow::buildChainSection() { chainList_.set_placeholder(*empty); chainList_.signal_row_selected().connect( [this](Gtk::ListBoxRow*) { onStageSelected(); }); - box->append(chainList_); + + auto* scroller = Gtk::make_managed(); + scroller->set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::AUTOMATIC); + scroller->set_child(chainList_); + scroller->set_size_request(-1, 150); + box->append(*scroller); // set_label() and set_icon_name() replace one another, so the icon and the // text are put together by hand. @@ -995,6 +1033,7 @@ void EffectorWindow::applyProject(const Project& project, const std::string& pat void EffectorWindow::clearSources() { while (auto* row = sourceList_.get_row_at_index(0)) sourceList_.remove(*row); + updateSourcesLabel(); mutateSources([](SourceList& list) { list.clear(); }); loadQueue_.clear(); if (loading_) dropLoadedImages_ = true; diff --git a/src/gui/window.hpp b/src/gui/window.hpp index 5c70fa5..99731e5 100644 --- a/src/gui/window.hpp +++ b/src/gui/window.hpp @@ -48,6 +48,7 @@ private: void closeLoadWindow(); void cancelLoad(); void appendSourceRow(const std::string& path); + void updateSourcesLabel(); // the collapsed expander shows the count void removeSource(Gtk::ListBoxRow* row); Gtk::Widget& buildChainSection(); @@ -139,6 +140,7 @@ private: std::unique_ptr loadWindow_; Gtk::ProgressBar* loadBar_ = nullptr; Gtk::Label* loadFileLabel_ = nullptr; + Gtk::Expander sourcesExpander_; Gtk::ListBox sourceList_; Gtk::SpinButton widthSpin_; Gtk::SpinButton heightSpin_;