diff --git a/libs/image/kis_base_node.h b/libs/image/kis_base_node.h index 9d1df1d16462eaf685f47211bcc812bdbd80f3ae..ffd8fc7518dd4170a084c9ae37113988c06a9e2b 100644 --- a/libs/image/kis_base_node.h +++ b/libs/image/kis_base_node.h @@ -92,24 +92,25 @@ public: bool stateInStasis; bool operator==(const Property &rhs) const { - return rhs.name == name && rhs.state == state; + return rhs.name == name && rhs.state == state && isInStasis == rhs.isInStasis; } - Property(): isMutable( false ) { } + Property(): isMutable( false ), isInStasis(false) { } /// Constructor for a mutable property. Property( const KoID &n, const QIcon &on, const QIcon &off, bool isOn ) - : id(n.id()), name( n.name() ), isMutable( true ), onIcon( on ), offIcon( off ), state( isOn ), canHaveStasis( false ) { } + : id(n.id()), name( n.name() ), isMutable( true ), onIcon( on ), offIcon( off ), state( isOn ), + canHaveStasis( false ), isInStasis(false) { } /** Constructor for a mutable property accepting stasis */ Property( const KoID &n, const QIcon &on, const QIcon &off, bool isOn, - bool _isInStasis, bool _stateInStasis ) + bool _isInStasis, bool _stateInStasis = false ) : id(n.id()), name(n.name()), isMutable( true ), onIcon( on ), offIcon( off ), state( isOn ), canHaveStasis( true ), isInStasis( _isInStasis ), stateInStasis( _stateInStasis ) { } /// Constructor for a nonmutable property. Property( const KoID &n, const QString &s ) - : id(n.id()), name(n.name()), isMutable( false ), state( s ) { } + : id(n.id()), name(n.name()), isMutable( false ), state( s ), isInStasis(false) { } }; /** Return this type for PropertiesRole. */ diff --git a/plugins/dockers/layerdocker/NodeDelegate.cpp b/plugins/dockers/layerdocker/NodeDelegate.cpp index 90ec1eec41ddf776c97121e4b0c9e99af678ecd9..ee7e333dd7ce35f309ce7c8d24689957cab006db 100644 --- a/plugins/dockers/layerdocker/NodeDelegate.cpp +++ b/plugins/dockers/layerdocker/NodeDelegate.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "kis_node_view_color_scheme.h" @@ -60,12 +61,28 @@ public: QColor checkersColor1; QColor checkersColor2; + QList shiftClickedIndexes; + + enum StasisOperation { + Record, + Review, + Restore + }; + QList rightmostProperties(const KisBaseNode::PropertyList &props) const; int numProperties(const QModelIndex &index) const; OptionalProperty findProperty(KisBaseNode::PropertyList &props, const OptionalProperty &refProp) const; OptionalProperty findVisibilityProperty(KisBaseNode::PropertyList &props) const; - void toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty prop, bool controlPressed, const QModelIndex &index); + void toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty prop, const Qt::KeyboardModifiers modifier, const QModelIndex &index); + void togglePropertyRecursive(const QModelIndex &root, const OptionalProperty &clickedProperty, const QList &items, StasisOperation record, bool mode); + + bool stasisIsDirty(const QModelIndex &root, const OptionalProperty &clickedProperty, bool on = false, bool off = false); + void resetPropertyStateRecursive(const QModelIndex &root, const OptionalProperty &clickedProperty); + + void getParentsIndex(QList &items, const QModelIndex &index); + void getChildrenIndex(QList &items, const QModelIndex &index); + void getSiblingsIndex(QList &items, const QModelIndex &index); }; NodeDelegate::NodeDelegate(NodeView *view, QObject *parent) @@ -76,6 +93,7 @@ NodeDelegate::NodeDelegate(NodeView *view, QObject *parent) QApplication::instance()->installEventFilter(this); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged())); + connect(this, SIGNAL(resetVisibilityStasis()), SLOT(slotResetState())); slotConfigChanged(); } @@ -480,6 +498,160 @@ OptionalProperty NodeDelegate::Private::findVisibilityProperty(KisBaseNode::Prop return 0; } +void NodeDelegate::Private::toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty clickedProperty, const Qt::KeyboardModifiers modifier, const QModelIndex &index) +{ + QModelIndex root(view->rootIndex()); + + if ((modifier & Qt::ShiftModifier) == Qt::ShiftModifier && clickedProperty->canHaveStasis) { + bool mode = true; + + OptionalProperty prop = findProperty(props, clickedProperty); + + // XXX: Change to use NodeProperty + int position = shiftClickedIndexes.indexOf(index); + + StasisOperation record = (!prop->isInStasis)? StasisOperation::Record : + (position < 0) ? StasisOperation::Review : StasisOperation::Restore; + + shiftClickedIndexes.clear(); + shiftClickedIndexes.push_back(index); + + QList items; + if (modifier == (Qt::ControlModifier | Qt::ShiftModifier)) { + mode = false; // inverted mode + items.insert(0, index); // important! + getSiblingsIndex(items, index); + } else { + getParentsIndex(items, index); + getChildrenIndex(items, index); + } + togglePropertyRecursive(root, clickedProperty, items, record, mode); + + } else { + shiftClickedIndexes.clear(); + resetPropertyStateRecursive(root, clickedProperty); + clickedProperty->state = !clickedProperty->state.toBool(); + clickedProperty->isInStasis = false; + view->model()->setData(index, QVariant::fromValue(props), KisNodeModel::PropertiesRole); + } +} + +void NodeDelegate::Private::togglePropertyRecursive(const QModelIndex &root, const OptionalProperty &clickedProperty, const QList &items, StasisOperation record, bool mode) +{ + int rowCount = view->model()->rowCount(root); + + for (int i = 0; i < rowCount; i++) { + QModelIndex idx = view->model()->index(i, 0, root); + + // The entire property list has to be altered because model->setData cannot set individual properties + KisBaseNode::PropertyList props = idx.data(KisNodeModel::PropertiesRole).value(); + OptionalProperty prop = findProperty(props, clickedProperty); + + if (!prop) continue; + + if (record == StasisOperation::Record) { + prop->stateInStasis = prop->state.toBool(); + } + if (record == StasisOperation::Review || record == StasisOperation::Record) { + prop->isInStasis = true; + if(mode) { //include mode + prop->state = (items.contains(idx))? QVariant(true) : QVariant(false); + } else { // exclude + prop->state = (!items.contains(idx))? prop->state : + (items.at(0) == idx)? QVariant(true) : QVariant(false); + } + } else { // restore + prop->state = QVariant(prop->stateInStasis); + prop->isInStasis = false; + } + + view->model()->setData(idx, QVariant::fromValue(props), KisNodeModel::PropertiesRole); + + togglePropertyRecursive(idx,clickedProperty, items, record, mode); + } +} + +bool NodeDelegate::Private::stasisIsDirty(const QModelIndex &root, const OptionalProperty &clickedProperty, bool on, bool off) +{ + + int rowCount = view->model()->rowCount(root); + bool result = false; + + for (int i = 0; i < rowCount; i++) { + if (result) break; // return on first find + QModelIndex idx = view->model()->index(i, 0, root); + // The entire property list has to be altered because model->setData cannot set individual properties + KisBaseNode::PropertyList props = idx.data(KisNodeModel::PropertiesRole).value(); + OptionalProperty prop = findProperty(props, clickedProperty); + + if (!prop) continue; + if (prop->isInStasis) { + on = true; + } else { + off = true; + } + // stop if both states exist + if (on && off) { + return true; + } + + result = stasisIsDirty(idx,clickedProperty, on, off); + } + return result; +} + +void NodeDelegate::Private::resetPropertyStateRecursive(const QModelIndex &root, const OptionalProperty &clickedProperty) +{ + if (!clickedProperty->canHaveStasis) return; + int rowCount = view->model()->rowCount(root); + + for (int i = 0; i < rowCount; i++) { + QModelIndex idx = view->model()->index(i, 0, root); + // The entire property list has to be altered because model->setData cannot set individual properties + KisBaseNode::PropertyList props = idx.data(KisNodeModel::PropertiesRole).value(); + OptionalProperty prop = findProperty(props, clickedProperty); + + if (!prop) continue; + prop->isInStasis = false; + view->model()->setData(idx, QVariant::fromValue(props), KisNodeModel::PropertiesRole); + + resetPropertyStateRecursive(idx,clickedProperty); + } +} + +void NodeDelegate::Private::getParentsIndex(QList &items, const QModelIndex &index) +{ + if (!index.isValid()) return; + items.append(index); + getParentsIndex(items, index.parent()); +} + +void NodeDelegate::Private::getChildrenIndex(QList &items, const QModelIndex &index) +{ + qint32 childs = view->model()->rowCount(index); + QModelIndex child; + // STEP 1: Go. + for (quint16 i = 0; i < childs; ++i) { + child = view->model()->index(i, 0, index); + items.append(child); + getChildrenIndex(items, child); + } +} + +void NodeDelegate::Private::getSiblingsIndex(QList &items, const QModelIndex &index) +{ + qint32 numberOfLeaves = view->model()->rowCount(index.parent()); + QModelIndex item; + // STEP 1: Go. + for (quint16 i = 0; i < numberOfLeaves; ++i) { + item = view->model()->index(i, 0, index.parent()); + if (item != index) { + items.append(item); + } + } +} + + void NodeDelegate::drawIcons(QPainter *p, const QStyleOptionViewItem &option, const QModelIndex &index) const { KisNodeViewColorScheme scm; @@ -599,8 +771,20 @@ void NodeDelegate::drawVisibilityIconHijack(QPainter *p, const QStyleOptionViewI p->setOpacity(0.35); } - p->drawPixmap(fitRect.x(), fitRect.center().y() - scm.visibilitySize() / 2, - icon.pixmap(scm.visibilitySize(), QIcon::Normal)); + QPixmap pixmapIcon(icon.pixmap(scm.visibilitySize(), QIcon::Active)); + p->drawPixmap(fitRect.x(), fitRect.center().y() - scm.visibilitySize() / 2, pixmapIcon); + + if (prop->isInStasis) { + QPainter::CompositionMode prevComposition = p->compositionMode(); + p->setCompositionMode(QPainter::CompositionMode_HardLight); + pixmapIcon = icon.pixmap(scm.visibilitySize(), QIcon::Active); + QBitmap mask = pixmapIcon.mask(); + pixmapIcon.fill(d->view->palette().color(QPalette::Highlight)); + pixmapIcon.setMask(mask); + p->drawPixmap(fitRect.x(), fitRect.center().y() - scm.visibilitySize() / 2, pixmapIcon); + p->setCompositionMode(prevComposition); + } + p->setOpacity(oldOpacity); //// For debugging purposes only @@ -660,58 +844,6 @@ void NodeDelegate::drawExpandButton(QPainter *p, const QStyleOptionViewItem &opt p->drawPixmap(rc.bottomLeft()-QPoint(0, scm.decorationSize()-1), pixmap); } -void NodeDelegate::Private::toggleProperty(KisBaseNode::PropertyList &props, OptionalProperty clickedProperty, bool controlPressed, const QModelIndex &index) -{ - QAbstractItemModel *model = view->model(); - - // Using Ctrl+click to enter stasis - if (controlPressed && clickedProperty->canHaveStasis) { - // STEP 0: Prepare to Enter or Leave control key stasis - quint16 numberOfLeaves = model->rowCount(index.parent()); - QModelIndex eachItem; - // STEP 1: Go. - if (clickedProperty->isInStasis == false) { // Enter - /* Make every leaf of this node go State = False, saving the old property value to stateInStasis */ - for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent()) - eachItem = model->index(i, 1, index.parent()); - // The entire property list has to be altered because model->setData cannot set individual properties - KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value(); - OptionalProperty prop = findProperty(eachPropertyList, clickedProperty); - if (!prop) continue; - prop->stateInStasis = prop->state.toBool(); - prop->state = eachItem == index; - prop->isInStasis = true; - model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole); - - } - - for (quint16 i = 0; i < numberOfLeaves; ++i) { // Foreach leaf in the node (index.parent()) - eachItem = model->index(i, 1, index.parent()); - KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value(); - OptionalProperty prop = findProperty(eachPropertyList, clickedProperty); - if (!prop) continue; - } - } else { // Leave - /* Make every leaf of this node go State = stateInStasis */ - for (quint16 i = 0; i < numberOfLeaves; ++i) { - eachItem = model->index(i, 1, index.parent()); - // The entire property list has to be altered because model->setData cannot set individual properties - KisBaseNode::PropertyList eachPropertyList = eachItem.data(KisNodeModel::PropertiesRole).value(); - OptionalProperty prop = findProperty(eachPropertyList, clickedProperty); - if (!prop) continue; - - prop->state = prop->stateInStasis; - prop->isInStasis = false; - model->setData(eachItem, QVariant::fromValue(eachPropertyList), KisNodeModel::PropertiesRole); - } - } - } else { - clickedProperty->state = !clickedProperty->state.toBool(); - model->setData(index, QVariant::fromValue(props), KisNodeModel::PropertiesRole); - } -} - - bool NodeDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) { KisNodeViewColorScheme scm; @@ -773,14 +905,16 @@ bool NodeDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const Q OptionalProperty clickedProperty = realProps[clickedIcon]; if (!clickedProperty) return false; - d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index); + d->toggleProperty(props, clickedProperty, mouseEvent->modifiers(), index); return true; } } else if (leftButton && visibilityClicked) { KisBaseNode::PropertyList props = index.data(KisNodeModel::PropertiesRole).value(); OptionalProperty clickedProperty = d->findVisibilityProperty(props); if (!clickedProperty) return false; - d->toggleProperty(props, clickedProperty, mouseEvent->modifiers() == Qt::ControlModifier, index); + + d->toggleProperty(props, clickedProperty, mouseEvent->modifiers(), index); + return true; } else if (leftButton && decorationClicked) { bool isExpandable = model->hasChildren(index); @@ -1012,3 +1146,19 @@ void NodeDelegate::slotUpdateIcon() { KisLayerPropertiesIcons::instance()->updateIcons(); } + +void NodeDelegate::slotResetState(){ + + NodeView *view = d->view; + QModelIndex root = view->rootIndex(); + int childs = view->model()->rowCount(root); + if (childs > 0){ + QModelIndex firstChild = view->model()->index(0, 0, root); + KisBaseNode::PropertyList props = firstChild.data(KisNodeModel::PropertiesRole).value(); + + OptionalProperty visibilityProperty = d->findVisibilityProperty(props); + if(d->stasisIsDirty(root, visibilityProperty)){ // clean inStasis if mixed! + d->resetPropertyStateRecursive(root, visibilityProperty); + } + } +} diff --git a/plugins/dockers/layerdocker/NodeDelegate.h b/plugins/dockers/layerdocker/NodeDelegate.h index de819b8357b210133ba485c7f4dedb9ccbaeb175..65771ad04acb9ad329bdbde25c9bae54077f4aca 100644 --- a/plugins/dockers/layerdocker/NodeDelegate.h +++ b/plugins/dockers/layerdocker/NodeDelegate.h @@ -50,6 +50,9 @@ public: void slotUpdateIcon(); +Q_SIGNALS: + void resetVisibilityStasis(); + protected: bool eventFilter(QObject *object, QEvent *event) override; @@ -83,6 +86,7 @@ private: private Q_SLOTS: void slotConfigChanged(); + void slotResetState(); }; #endif diff --git a/plugins/dockers/layerdocker/NodeView.cpp b/plugins/dockers/layerdocker/NodeView.cpp index 8c18321979bceb0368ad68fb1c55009f5d39933a..50a8eac46f77c8a0e98e32dce8e9f14adce2053f 100644 --- a/plugins/dockers/layerdocker/NodeView.cpp +++ b/plugins/dockers/layerdocker/NodeView.cpp @@ -149,6 +149,7 @@ void NodeView::addPropertyActions(QMenu *menu, const QModelIndex &index) void NodeView::updateNode(const QModelIndex &index) { dataChanged(index, index); + d->delegate.resetVisibilityStasis(); } QItemSelectionModel::SelectionFlags NodeView::selectionCommand(const QModelIndex &index,