bool KnobGui::isSecretRecursive() const { // If the Knob is within a group, only show it if the group is unfolded! // To test it: // try TuttlePinning: fold all groups, then switch from perspective to affine to perspective. // VISIBILITY is different from SECRETNESS. The code considers that both things are equivalent, which is wrong. // Of course, this check has to be *recursive* (in case the group is within a folded group) KnobPtr knob = getKnob(); bool isViewerKnob = _imp->container->isInViewerUIKnob(); bool showit = isViewerKnob ? !knob->getInViewerContextSecret() : !knob->getIsSecret(); KnobPtr parentKnob = knob->getParentKnob(); KnobGroup* parentIsGroup = dynamic_cast<KnobGroup*>( parentKnob.get() ); while (showit && parentKnob && parentIsGroup) { KnobGuiGroup* parentGui = dynamic_cast<KnobGuiGroup*>( _imp->container->getKnobGui(parentKnob).get() ); // check for secretness and visibility of the group bool parentSecret = isViewerKnob ? parentKnob->getInViewerContextSecret() : parentKnob->getIsSecret(); if ( parentSecret || ( parentGui && !parentGui->isChecked() ) ) { showit = false; // one of the including groups is folder, so this item is hidden } // prepare for next loop iteration parentKnob = parentKnob->getParentKnob(); parentIsGroup = dynamic_cast<KnobGroup*>( parentKnob.get() ); } return !showit; }
void KnobGui::setSecret() { bool showit = !isSecretRecursive(); if (showit) { show(); // } else { hide(); } KnobGuiGroup* isGrp = dynamic_cast<KnobGuiGroup*>(this); if (isGrp) { const std::list<KnobGuiWPtr>& children = isGrp->getChildren(); for (std::list<KnobGuiWPtr>::const_iterator it = children.begin(); it != children.end(); ++it) { KnobGuiPtr k = it->lock(); if (!k) { continue; } k->setSecret(); } } }
KnobGuiPtr DockablePanelPrivate::findKnobGuiOrCreate(const KnobPtr & knob, bool makeNewLine, QWidget* lastRowWidget, const std::vector< boost::shared_ptr< KnobI > > & knobsOnSameLine) { assert(knob); boost::shared_ptr<KnobGroup> isGroup = boost::dynamic_pointer_cast<KnobGroup>(knob); boost::shared_ptr<KnobPage> isPage = boost::dynamic_pointer_cast<KnobPage>(knob); for (KnobsGuiMapping::const_iterator it = _knobs.begin(); it != _knobs.end(); ++it) { if ( (it->first.lock() == knob) && it->second ) { if (isPage) { return it->second; } else if ( isGroup && ( ( !isGroup->isTab() && it->second->hasWidgetBeenCreated() ) || isGroup->isTab() ) ) { return it->second; } else if ( it->second->hasWidgetBeenCreated() ) { return it->second; } else { break; } } } if (isPage) { if ( isPage->getChildren().empty() ) { return KnobGuiPtr(); } getOrCreatePage(isPage); KnobsVec children = isPage->getChildren(); initializeKnobVector(children, lastRowWidget); return KnobGuiPtr(); } KnobGuiPtr ret = createKnobGui(knob); if (!ret) { return KnobGuiPtr(); } KnobPtr parentKnob = knob->getParentKnob(); boost::shared_ptr<KnobGroup> parentIsGroup = boost::dynamic_pointer_cast<KnobGroup>(parentKnob); KnobGuiGroup* parentGui = 0; /// if this knob is within a group, make sure the group is created so far if (parentIsGroup) { parentGui = dynamic_cast<KnobGuiGroup*>( findKnobGuiOrCreate( parentKnob, true, ret->getFieldContainer() ).get() ); } ///So far the knob could have no parent, in which case we force it to be in the default page. if (!parentKnob) { boost::shared_ptr<KnobPage> defPage = ensureDefaultPageKnobCreated(); defPage->addKnob(knob); parentKnob = defPage; } ///if widgets for the KnobGui have already been created, don't do the following ///For group only create the gui if it is not a tab. if ( isGroup && isGroup->isTab() ) { boost::shared_ptr<KnobPage> parentIsPage = boost::dynamic_pointer_cast<KnobPage>(parentKnob); if (!parentKnob || parentIsPage) { PageMap::iterator page = _pages.end(); if (!parentKnob) { page = getDefaultPage(knob); } else { page = getOrCreatePage(parentIsPage); } bool existed = true; if (!page->second.groupAsTab) { existed = false; page->second.groupAsTab = new TabGroup(_publicInterface); } page->second.groupAsTab->addTab( isGroup, QString::fromUtf8( isGroup->getLabel().c_str() ) ); ///retrieve the form layout QGridLayout* layout; if (_useScrollAreasForTabs) { layout = dynamic_cast<QGridLayout*>( dynamic_cast<QScrollArea*>(page->second.tab)->widget()->layout() ); } else { layout = dynamic_cast<QGridLayout*>( page->second.tab->layout() ); } assert(layout); if (!existed) { layout->addWidget(page->second.groupAsTab, page->second.currentRow, 0, 1, 2); } page->second.groupAsTab->refreshTabSecretNess( isGroup.get() ); } else { assert(parentIsGroup); assert(parentGui); TabGroup* groupAsTab = parentGui->getOrCreateTabWidget(); groupAsTab->addTab( isGroup, QString::fromUtf8( isGroup->getLabel().c_str() ) ); if ( parentIsGroup && parentIsGroup->isTab() ) { ///insert the tab in the layout of the parent ///Find the page in the parentParent group KnobPtr parentParent = parentKnob->getParentKnob(); assert(parentParent); boost::shared_ptr<KnobGroup> parentParentIsGroup = boost::dynamic_pointer_cast<KnobGroup>(parentParent); boost::shared_ptr<KnobPage> parentParentIsPage = boost::dynamic_pointer_cast<KnobPage>(parentParent); assert(parentParentIsGroup || parentParentIsPage); TabGroup* parentTabGroup = 0; if (parentParentIsPage) { PageMap::iterator page = getOrCreatePage(parentParentIsPage); assert( page != _pages.end() ); parentTabGroup = page->second.groupAsTab; } else { KnobsGuiMapping::iterator it = findKnobGui(parentParent); assert( it != _knobs.end() ); KnobGuiGroup* parentParentGroupGui = dynamic_cast<KnobGuiGroup*>( it->second.get() ); assert(parentParentGroupGui); parentTabGroup = parentParentGroupGui->getOrCreateTabWidget(); } QGridLayout* layout = parentTabGroup->addTab( parentIsGroup, QString::fromUtf8( parentIsGroup->getLabel().c_str() ) ); assert(layout); layout->addWidget(groupAsTab, 0, 0, 1, 2); } else { boost::shared_ptr<KnobPage> topLevelPage = knob->getTopLevelPage(); PageMap::iterator page = getOrCreatePage(topLevelPage); assert( page != _pages.end() ); ///retrieve the form layout QGridLayout* layout; if (_useScrollAreasForTabs) { layout = dynamic_cast<QGridLayout*>( dynamic_cast<QScrollArea*>(page->second.tab)->widget()->layout() ); } else { layout = dynamic_cast<QGridLayout*>( page->second.tab->layout() ); } assert(layout); layout->addWidget(groupAsTab, page->second.currentRow, 0, 1, 2); } groupAsTab->refreshTabSecretNess( isGroup.get() ); } } else if ( !ret->hasWidgetBeenCreated() ) { KnobPtr parentKnobTmp = parentKnob; while (parentKnobTmp) { KnobPtr parent = parentKnobTmp->getParentKnob(); if (!parent) { break; } else { parentKnobTmp = parent; } } ////find in which page the knob should be boost::shared_ptr<KnobPage> isTopLevelParentAPage = boost::dynamic_pointer_cast<KnobPage>(parentKnobTmp); assert(isTopLevelParentAPage); PageMap::iterator page = getOrCreatePage(isTopLevelParentAPage); assert( page != _pages.end() ); ///retrieve the form layout QGridLayout* layout; if (_useScrollAreasForTabs) { layout = dynamic_cast<QGridLayout*>( dynamic_cast<QScrollArea*>(page->second.tab)->widget()->layout() ); } else { layout = dynamic_cast<QGridLayout*>( page->second.tab->layout() ); } assert(layout); ///if the knob has specified that it didn't want to trigger a new line, decrement the current row /// index of the tab if (!makeNewLine) { --page->second.currentRow; } QWidget* fieldContainer = 0; QHBoxLayout* fieldLayout = 0; if (makeNewLine) { ///if new line is not turned off, create a new line fieldContainer = new QWidget(page->second.tab); fieldLayout = new QHBoxLayout(fieldContainer); fieldLayout->setContentsMargins( TO_DPIX(3), 0, 0, TO_DPIY(NATRON_SETTINGS_VERTICAL_SPACING_PIXELS) ); fieldLayout->setSpacing( TO_DPIY(2) ); fieldLayout->setAlignment(Qt::AlignLeft); } else { ///otherwise re-use the last row's widget and layout assert(lastRowWidget); fieldContainer = lastRowWidget; fieldLayout = dynamic_cast<QHBoxLayout*>( fieldContainer->layout() ); } assert(fieldContainer); assert(fieldLayout); ///Create the label if needed KnobClickableLabel* label = 0; Label* warningLabel = 0; std::string descriptionLabel; KnobString* isStringKnob = dynamic_cast<KnobString*>( knob.get() ); bool isLabelKnob = isStringKnob && isStringKnob->isLabel(); if (isLabelKnob) { descriptionLabel = isStringKnob->getValue(); } else { descriptionLabel = knob->getLabel(); } const std::string& labelIconFilePath = knob->getIconLabel(); QWidget *labelContainer = 0; QHBoxLayout *labelLayout = 0; const bool hasLabel = ret->isLabelVisible() || isLabelKnob; if (hasLabel) { if (makeNewLine) { labelContainer = new QWidget(page->second.tab); labelLayout = new QHBoxLayout(labelContainer); labelLayout->setContentsMargins( TO_DPIX(3), 0, 0, TO_DPIY(NATRON_SETTINGS_VERTICAL_SPACING_PIXELS) ); labelLayout->setSpacing( TO_DPIY(2) ); } label = new KnobClickableLabel(QString(), ret, page->second.tab); warningLabel = new Label(page->second.tab); warningLabel->setVisible(false); QFontMetrics fm(label->font(), 0); int pixSize = fm.height(); QPixmap stdErrorPix; stdErrorPix = getStandardIcon(QMessageBox::Critical, pixSize, label); warningLabel->setPixmap(stdErrorPix); bool pixmapSet = false; if ( !labelIconFilePath.empty() ) { QPixmap pix; if (labelIconFilePath == "dialog-warning") { pix = getStandardIcon(QMessageBox::Warning, pixSize, label); } else if (labelIconFilePath == "dialog-question") { pix = getStandardIcon(QMessageBox::Question, pixSize, label); } else if (labelIconFilePath == "dialog-error") { pix = stdErrorPix; } else if (labelIconFilePath == "dialog-information") { pix = getStandardIcon(QMessageBox::Information, pixSize, label); } else { pix.load( QString::fromUtf8( labelIconFilePath.c_str() ) ); if (pix.width() != pixSize) { pix = pix.scaled(pixSize, pixSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } } if ( !pix.isNull() ) { pixmapSet = true; label->setPixmap(pix); } } if (!pixmapSet) { QString labelStr( QString::fromUtf8( descriptionLabel.c_str() ) ); /*labelStr += ":";*/ if ( ret->isLabelBold() ) { label->setBold(true); } label->setText_overload(labelStr ); } QObject::connect( label, SIGNAL(clicked(bool)), ret.get(), SIGNAL(labelClicked(bool)) ); if (makeNewLine) { labelLayout->addWidget(warningLabel); labelLayout->addWidget(label); } } /* * Find out in which layout the knob should be: either in the layout of the page or in the layout of * the nearest parent group tab in the hierarchy */ boost::shared_ptr<KnobGroup> closestParentGroupTab; KnobPtr parentTmp = parentKnob; assert(parentKnobTmp); while (!closestParentGroupTab) { boost::shared_ptr<KnobGroup> parentGroup = boost::dynamic_pointer_cast<KnobGroup>(parentTmp); if ( parentGroup && parentGroup->isTab() ) { closestParentGroupTab = parentGroup; } parentTmp = parentTmp->getParentKnob(); if (!parentTmp) { break; } } if (closestParentGroupTab) { /* * At this point we know that the parent group (which is a tab in the TabWidget) will have at least 1 knob * so ensure it is added to the TabWidget. * There are 2 possibilities, either the parent of the group tab is another group, in which case we have to * make sure the TabWidget is visible in the parent TabWidget of the group, otherwise we just add the TabWidget * to the on of the page. */ KnobPtr parentParent = closestParentGroupTab->getParentKnob(); KnobGroup* parentParentIsGroup = dynamic_cast<KnobGroup*>( parentParent.get() ); boost::shared_ptr<KnobPage> parentParentIsPage = boost::dynamic_pointer_cast<KnobPage>(parentParent); assert(parentParentIsGroup || parentParentIsPage); if (parentParentIsGroup) { KnobGuiGroup* parentParentGroupGui = dynamic_cast<KnobGuiGroup*>( findKnobGuiOrCreate( parentParent, true, ret->getFieldContainer() ).get() ); assert(parentParentGroupGui); if (parentParentGroupGui) { TabGroup* groupAsTab = parentParentGroupGui->getOrCreateTabWidget(); assert(groupAsTab); layout = groupAsTab->addTab( closestParentGroupTab, QString::fromUtf8( closestParentGroupTab->getLabel().c_str() ) ); } } else if (parentParentIsPage) { PageMap::iterator page = getOrCreatePage(parentParentIsPage); assert( page != _pages.end() ); assert(page->second.groupAsTab); layout = page->second.groupAsTab->addTab( closestParentGroupTab, QString::fromUtf8( closestParentGroupTab->getLabel().c_str() ) ); } assert(layout); } ///fill the fieldLayout with the widgets ret->createGUI(layout, fieldContainer, labelContainer, label, warningLabel, fieldLayout, makeNewLine, knobsOnSameLine); ret->setEnabledSlot(); ///Must add the row to the layout before calling setSecret() if (makeNewLine) { int rowIndex; if (closestParentGroupTab) { rowIndex = layout->rowCount(); } else if ( parentGui && knob->isDynamicallyCreated() ) { const std::list<KnobGuiWPtr>& children = parentGui->getChildren(); if ( children.empty() ) { rowIndex = parentGui->getActualIndexInLayout(); } else { rowIndex = children.back().lock()->getActualIndexInLayout(); } ++rowIndex; } else { rowIndex = page->second.currentRow; } const bool labelOnSameColumn = ret->isLabelOnSameColumn(); if (!hasLabel) { layout->addWidget(fieldContainer, rowIndex, 0, 1, 2); } else { if (label) { if (labelOnSameColumn) { labelLayout->addWidget(fieldContainer); layout->addWidget(labelContainer, rowIndex, 0, 1, 2); } else { layout->addWidget(labelContainer, rowIndex, 0, 1, 1, Qt::AlignRight); layout->addWidget(fieldContainer, rowIndex, 1, 1, 1); } } } //if (closestParentGroupTab) { ///See http://stackoverflow.com/questions/14033902/qt-qgridlayout-automatically-centers-moves-items-to-the-middle for ///a bug of QGridLayout: basically all items are centered, but we would like to add stretch in the bottom of the layout. ///To do this we add an empty widget with an expanding vertical size policy. /*QWidget* foundSpacer = 0; for (int i = 0; i < layout->rowCount(); ++i) { QLayoutItem* item = layout->itemAtPosition(i, 0); if (!item) { continue; } QWidget* w = item->widget(); if (!w) { continue; } if (w->objectName() == QString::fromUtf8("emptyWidget")) { foundSpacer = w; break; } } if (foundSpacer) { layout->removeWidget(foundSpacer); } else { foundSpacer = new QWidget(layout->parentWidget()); foundSpacer->setObjectName(QString::fromUtf8("emptyWidget")); foundSpacer->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); } ///And add our stretch layout->addWidget(foundSpacer,layout->rowCount(), 0, 1, 2);*/ // } } // makeNewLine ret->setSecret(); if ( knob->isNewLineActivated() && ret->shouldAddStretch() ) { fieldLayout->addStretch(); } ///increment the row count ++page->second.currentRow; if (parentIsGroup && parentGui) { parentGui->addKnob(ret); } } // if ( !ret->hasWidgetBeenCreated() && ( !isGroup || !isGroup->isTab() ) ) {