diff --git a/libs/global/kis_acyclic_signal_connector.cpp b/libs/global/kis_acyclic_signal_connector.cpp index 85b9afeae8..b7c7a77c9e 100644 --- a/libs/global/kis_acyclic_signal_connector.cpp +++ b/libs/global/kis_acyclic_signal_connector.cpp @@ -1,307 +1,312 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_acyclic_signal_connector.h" #include "kis_debug.h" KisAcyclicSignalConnector::KisAcyclicSignalConnector(QObject *parent) : QObject(parent), m_signalsBlocked(0) { } KisAcyclicSignalConnector::~KisAcyclicSignalConnector() { } void KisAcyclicSignalConnector::connectForwardDouble(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(forwardSlotDouble(double)), Qt::UniqueConnection); connect(this, SIGNAL(forwardSignalDouble(double)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectBackwardDouble(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(backwardSlotDouble(double)), Qt::UniqueConnection); connect(this, SIGNAL(backwardSignalDouble(double)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectForwardInt(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(forwardSlotInt(int)), Qt::UniqueConnection); connect(this, SIGNAL(forwardSignalInt(int)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectBackwardInt(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(backwardSlotInt(int)), Qt::UniqueConnection); connect(this, SIGNAL(backwardSignalInt(int)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectForwardBool(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(forwardSlotBool(bool)), Qt::UniqueConnection); connect(this, SIGNAL(forwardSignalBool(bool)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectBackwardBool(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(backwardSlotBool(bool)), Qt::UniqueConnection); connect(this, SIGNAL(backwardSignalBool(bool)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectForwardVoid(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(forwardSlotVoid()), Qt::UniqueConnection); connect(this, SIGNAL(forwardSignalVoid()), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectBackwardVoid(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(backwardSlotVoid()), Qt::UniqueConnection); connect(this, SIGNAL(backwardSignalVoid()), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectForwardVariant(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(forwardSlotVariant(QVariant)), Qt::UniqueConnection); connect(this, SIGNAL(forwardSignalVariant(QVariant)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectBackwardVariant(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(backwardSlotVariant(QVariant)), Qt::UniqueConnection); connect(this, SIGNAL(backwardSignalVariant(QVariant)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectForwardResourcePair(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(forwardSlotResourcePair(int,QVariant)), Qt::UniqueConnection); connect(this, SIGNAL(forwardSignalResourcePair(int,QVariant)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectBackwardResourcePair(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(backwardSlotResourcePair(int,QVariant)), Qt::UniqueConnection); connect(this, SIGNAL(backwardSignalResourcePair(int,QVariant)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectForwardKoColor(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(forwardSlotKoColor(KoColor)), Qt::UniqueConnection); connect(this, SIGNAL(forwardSignalKoColor(KoColor)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::connectBackwardKoColor(QObject *sender, const char *signal, QObject *receiver, const char *method) { connect(sender, signal, this, SLOT(backwardSlotKoColor(KoColor)), Qt::UniqueConnection); connect(this, SIGNAL(backwardSignalKoColor(KoColor)), receiver, method, Qt::UniqueConnection); } void KisAcyclicSignalConnector::lock() { if (m_parentConnector) { m_parentConnector->lock(); } else { coordinatedLock(); Q_FOREACH(QPointer conn, m_coordinatedConnectors) { if (!conn) continue; conn->coordinatedLock(); } } } void KisAcyclicSignalConnector::unlock() { if (m_parentConnector) { m_parentConnector->unlock(); } else { Q_FOREACH(QPointer conn, m_coordinatedConnectors) { if (!conn) continue; conn->coordinatedUnlock(); } coordinatedUnlock(); } } +bool KisAcyclicSignalConnector::isLocked() const +{ + return m_signalsBlocked; +} + void KisAcyclicSignalConnector::coordinatedLock() { m_signalsBlocked++; } void KisAcyclicSignalConnector::coordinatedUnlock() { m_signalsBlocked--; } KisAcyclicSignalConnector *KisAcyclicSignalConnector::createCoordinatedConnector() { KisAcyclicSignalConnector *conn = new KisAcyclicSignalConnector(this); conn->m_parentConnector = this; m_coordinatedConnectors.append(conn); return conn; } void KisAcyclicSignalConnector::forwardSlotDouble(double value) { if (m_signalsBlocked) return; lock(); emit forwardSignalDouble(value); unlock(); } void KisAcyclicSignalConnector::backwardSlotDouble(double value) { if (m_signalsBlocked) return; lock(); emit backwardSignalDouble(value); unlock(); } void KisAcyclicSignalConnector::forwardSlotInt(int value) { if (m_signalsBlocked) return; lock(); emit forwardSignalInt(value); unlock(); } void KisAcyclicSignalConnector::backwardSlotInt(int value) { if (m_signalsBlocked) return; lock(); emit backwardSignalInt(value); unlock(); } void KisAcyclicSignalConnector::forwardSlotBool(bool value) { if (m_signalsBlocked) return; lock(); emit forwardSignalBool(value); unlock(); } void KisAcyclicSignalConnector::backwardSlotBool(bool value) { if (m_signalsBlocked) return; lock(); emit backwardSignalBool(value); unlock(); } void KisAcyclicSignalConnector::forwardSlotVoid() { if (m_signalsBlocked) return; lock(); emit forwardSignalVoid(); unlock(); } void KisAcyclicSignalConnector::backwardSlotVoid() { if (m_signalsBlocked) return; lock(); emit backwardSignalVoid(); unlock(); } void KisAcyclicSignalConnector::forwardSlotVariant(const QVariant &value) { if (m_signalsBlocked) return; lock(); emit forwardSignalVariant(value); unlock(); } void KisAcyclicSignalConnector::backwardSlotVariant(const QVariant &value) { if (m_signalsBlocked) return; lock(); emit backwardSignalVariant(value); unlock(); } void KisAcyclicSignalConnector::forwardSlotResourcePair(int key, const QVariant &resource) { if (m_signalsBlocked) return; lock(); emit forwardSignalResourcePair(key, resource); unlock(); } void KisAcyclicSignalConnector::backwardSlotResourcePair(int key, const QVariant &resource) { if (m_signalsBlocked) return; lock(); emit backwardSignalResourcePair(key, resource); unlock(); } void KisAcyclicSignalConnector::forwardSlotKoColor(const KoColor &value) { if (m_signalsBlocked) return; lock(); emit forwardSignalKoColor(value); unlock(); } void KisAcyclicSignalConnector::backwardSlotKoColor(const KoColor &value) { if (m_signalsBlocked) return; lock(); emit backwardSignalKoColor(value); unlock(); } diff --git a/libs/global/kis_acyclic_signal_connector.h b/libs/global/kis_acyclic_signal_connector.h index 3b32d2194e..400f0b7ea0 100644 --- a/libs/global/kis_acyclic_signal_connector.h +++ b/libs/global/kis_acyclic_signal_connector.h @@ -1,202 +1,208 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KIS_ACYCLIC_SIGNAL_CONNECTOR_H #define __KIS_ACYCLIC_SIGNAL_CONNECTOR_H #include #include "kritaglobal_export.h" #include class KisAcyclicSignalConnector; class KoColor; #include #include /** * A special class for connecting UI elements to manager classes. * It allows to avoid direct calling blockSignals() for the sender UI * element all the time. This is the most important when the measured * value can be changed not only by the user through the UI, but also * by the manager according to some internal rules. * * Example: * * Suppose we have the following connections: * * 1) QDoubleSpinBox::valueChanged(double) -> Manager::slotSetValue(double) * 2) Manager::valueChanged(double) -> QDoubleSpinBox::setValue(double) * * Now if the manager decides to change/correct the value, the spinbox * will go into an infinite loop. * * See an example in KisToolCropConfigWidget. * * NOTE (coordinated connectors): * * Please make sure that you don't convert more than one forward and one backward * connection to the connector! If you do so, they will become connected to the * same forwarding slot and, therefore, both output signals will be emitted on * every incoming signal. * * To connect multiple connections that block recursive calls, please use * "coordinated connectors". Each such connector will have two more connection * slots that you can reuse. * */ class KRITAGLOBAL_EXPORT KisAcyclicSignalConnector : public QObject { Q_OBJECT public: typedef std::unique_lock Blocker; public: KisAcyclicSignalConnector(QObject *parent = 0); ~KisAcyclicSignalConnector(); void connectForwardDouble(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectBackwardDouble(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectForwardInt(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectBackwardInt(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectForwardBool(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectBackwardBool(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectForwardVoid(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectBackwardVoid(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectForwardVariant(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectBackwardVariant(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectForwardResourcePair(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectBackwardResourcePair(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectForwardKoColor(QObject *sender, const char *signal, QObject *receiver, const char *method); void connectBackwardKoColor(QObject *sender, const char *signal, QObject *receiver, const char *method); /** * Lock the connector and all its coordinated child connectors */ void lock(); /** * Unlock the connector and all its coordinated child connectors */ void unlock(); + /** + * \return true if the connector is locked by some signal or manually. + * Used for debugging purposes mostly. + */ + bool isLocked() const; + /** * @brief create a coordinated connector that can be used for extending * the number of self-locking connection. * * The coordinated connector can be used to extend the number of self-locking * connections. Each coordinated connector adds two more connection slots (forward * and backward). Lock of any connector in a coordinated group will lock the whole * group. * * The created connector is owned by *this, don't delete it! */ KisAcyclicSignalConnector *createCoordinatedConnector(); private: /** * Lock this connector only. */ void coordinatedLock(); /** * Unlock this connector only. */ void coordinatedUnlock(); private Q_SLOTS: void forwardSlotDouble(double value); void backwardSlotDouble(double value); void forwardSlotInt(int value); void backwardSlotInt(int value); void forwardSlotBool(bool value); void backwardSlotBool(bool value); void forwardSlotVoid(); void backwardSlotVoid(); void forwardSlotVariant(const QVariant &value); void backwardSlotVariant(const QVariant &value); void forwardSlotResourcePair(int key, const QVariant &resource); void backwardSlotResourcePair(int key, const QVariant &resource); void forwardSlotKoColor(const KoColor &value); void backwardSlotKoColor(const KoColor &value); Q_SIGNALS: void forwardSignalDouble(double value); void backwardSignalDouble(double value); void forwardSignalInt(int value); void backwardSignalInt(int value); void forwardSignalBool(bool value); void backwardSignalBool(bool value); void forwardSignalVoid(); void backwardSignalVoid(); void forwardSignalVariant(const QVariant &value); void backwardSignalVariant(const QVariant &value); void forwardSignalResourcePair(int key, const QVariant &value); void backwardSignalResourcePair(int key, const QVariant &value); void forwardSignalKoColor(const KoColor &value); void backwardSignalKoColor(const KoColor &value); private: int m_signalsBlocked; QVector> m_coordinatedConnectors; QPointer m_parentConnector; }; #endif /* __KIS_ACYCLIC_SIGNAL_CONNECTOR_H */ diff --git a/libs/ui/widgets/KoFillConfigWidget.cpp b/libs/ui/widgets/KoFillConfigWidget.cpp index 11938edf3e..603a748854 100644 --- a/libs/ui/widgets/KoFillConfigWidget.cpp +++ b/libs/ui/widgets/KoFillConfigWidget.cpp @@ -1,898 +1,926 @@ /* This file is part of the KDE project * Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr) * Copyright (C) 2012 Jean-Nicolas Artaud * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoFillConfigWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoResourceServerProvider.h" #include "KoResourceServerAdapter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoZoomHandler.h" #include "KoColorPopupButton.h" #include "ui_KoFillConfigWidget.h" #include #include #include #include #include "kis_canvas_resource_provider.h" #include #include #include #include "kis_global.h" #include "kis_debug.h" static const char* const buttonnone[]={ "16 16 3 1", "# c #000000", "e c #ff0000", "- c #ffffff", "################", "#--------------#", "#-e----------e-#", "#--e--------e--#", "#---e------e---#", "#----e----e----#", "#-----e--e-----#", "#------ee------#", "#------ee------#", "#-----e--e-----#", "#----e----e----#", "#---e------e---#", "#--e--------e--#", "#-e----------e-#", "#--------------#", "################"}; static const char* const buttonsolid[]={ "16 16 2 1", "# c #000000", ". c #969696", "################", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "#..............#", "################"}; // FIXME: Smoother gradient button. static const char* const buttongradient[]={ "16 16 15 1", "# c #000000", "n c #101010", "m c #202020", "l c #303030", "k c #404040", "j c #505050", "i c #606060", "h c #707070", "g c #808080", "f c #909090", "e c #a0a0a0", "d c #b0b0b0", "c c #c0c0c0", "b c #d0d0d0", "a c #e0e0e0", "################", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "#abcdefghijklmn#", "################"}; static const char* const buttonpattern[]={ "16 16 4 1", ". c #0a0a0a", "# c #333333", "a c #a0a0a0", "b c #ffffffff", "################", "#aaaaabbbbaaaaa#", "#aaaaabbbbaaaaa#", "#aaaaabbbbaaaaa#", "#aaaaabbbbaaaaa#", "#aaaaabbbbaaaaa#", "#bbbbbaaaabbbbb#", "#bbbbbaaaabbbbb#", "#bbbbbaaaabbbbb#", "#bbbbbaaaabbbbb#", "#aaaaabbbbaaaaa#", "#aaaaabbbbaaaaa#", "#aaaaabbbbaaaaa#", "#aaaaabbbbaaaaa#", "#aaaaabbbbaaaaa#", "################"}; class Q_DECL_HIDDEN KoFillConfigWidget::Private { public: Private(KoFlake::FillVariant _fillVariant) : canvas(0), colorChangedCompressor(100, KisSignalCompressor::FIRST_ACTIVE), gradientChangedCompressor(100, KisSignalCompressor::FIRST_ACTIVE), shapeChangedCompressor(200,KisSignalCompressor::FIRST_ACTIVE), fillVariant(_fillVariant), noSelectionTrackingMode(false) { } KoColorPopupAction *colorAction; KoResourcePopupAction *gradientAction; KoResourcePopupAction *patternAction; QButtonGroup *group; KoCanvasBase *canvas; KisSignalCompressor colorChangedCompressor; KisAcyclicSignalConnector shapeChangedAcyclicConnector; KisAcyclicSignalConnector resourceManagerAcyclicConnector; KoFillConfigWidget::StyleButton selectedFillIndex {KoFillConfigWidget::None}; QSharedPointer activeGradient; KisSignalCompressor gradientChangedCompressor; KisSignalCompressor shapeChangedCompressor; KoFlake::FillVariant fillVariant; - - QList previousShapeSelected;/// container to see if the selection has actually changed - bool noSelectionTrackingMode; Ui_KoFillConfigWidget *ui; std::vector deactivationLocks; + + boost::optional overriddenColorFromProvider; }; KoFillConfigWidget::KoFillConfigWidget(KoCanvasBase *canvas, KoFlake::FillVariant fillVariant, bool trackShapeSelection, QWidget *parent) : QWidget(parent) , d(new Private(fillVariant)) { d->canvas = canvas; if (trackShapeSelection) { d->shapeChangedAcyclicConnector.connectBackwardVoid( d->canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), &d->shapeChangedCompressor, SLOT(start())); d->shapeChangedAcyclicConnector.connectBackwardVoid( d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), &d->shapeChangedCompressor, SLOT(start())); connect(&d->shapeChangedCompressor, SIGNAL(timeout()), this, SLOT(shapeChanged())); } d->resourceManagerAcyclicConnector.connectBackwardResourcePair( d->canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(slotCanvasResourceChanged(int,QVariant))); d->resourceManagerAcyclicConnector.connectForwardVoid( this, SIGNAL(sigInternalRequestColorToResourceManager()), this, SLOT(slotProposeCurrentColorToResourceManager())); + KisAcyclicSignalConnector *resetConnector = d->resourceManagerAcyclicConnector.createCoordinatedConnector(); + resetConnector->connectForwardVoid( + this, SIGNAL(sigInternalRecoverColorInResourceManager()), + this, SLOT(slotRecoverColorInResourceManager())); // configure GUI d->ui = new Ui_KoFillConfigWidget(); d->ui->setupUi(this); d->group = new QButtonGroup(this); d->group->setExclusive(true); d->ui->btnNoFill->setIcon(QPixmap((const char **) buttonnone)); d->group->addButton(d->ui->btnNoFill, None); d->ui->btnSolidFill->setIcon(QPixmap((const char **) buttonsolid)); d->group->addButton(d->ui->btnSolidFill, Solid); d->ui->btnGradientFill->setIcon(QPixmap((const char **) buttongradient)); d->group->addButton(d->ui->btnGradientFill, Gradient); d->ui->btnPatternFill->setIcon(QPixmap((const char **) buttonpattern)); d->group->addButton(d->ui->btnPatternFill, Pattern); d->ui->btnPatternFill->setVisible(false); d->colorAction = new KoColorPopupAction(d->ui->btnChooseSolidColor); d->colorAction->setToolTip(i18n("Change the filling color")); d->colorAction->setCurrentColor(Qt::white); d->ui->btnChooseSolidColor->setDefaultAction(d->colorAction); d->ui->btnChooseSolidColor->setPopupMode(QToolButton::InstantPopup); d->ui->btnSolidColorPick->setIcon(KisIconUtils::loadIcon("krita_tool_color_picker")); // TODO: for now the color picking button is disabled! d->ui->btnSolidColorPick->setEnabled(false); d->ui->btnSolidColorPick->setVisible(false); connect(d->colorAction, SIGNAL(colorChanged(KoColor)), &d->colorChangedCompressor, SLOT(start())); connect(&d->colorChangedCompressor, SIGNAL(timeout()), SLOT(colorChanged())); connect(d->ui->btnChooseSolidColor, SIGNAL(iconSizeChanged()), d->colorAction, SLOT(updateIcon())); connect(d->group, SIGNAL(buttonClicked(int)), SLOT(styleButtonPressed(int))); connect(d->group, SIGNAL(buttonClicked(int)), SLOT(slotUpdateFillTitle())); slotUpdateFillTitle(); styleButtonPressed(d->group->checkedId()); // Gradient selector d->ui->wdgGradientEditor->setCompactMode(true); connect(d->ui->wdgGradientEditor, SIGNAL(sigGradientChanged()), &d->gradientChangedCompressor, SLOT(start())); connect(&d->gradientChangedCompressor, SIGNAL(timeout()), SLOT(activeGradientChanged())); KoResourceServerProvider *serverProvider = KoResourceServerProvider::instance(); QSharedPointer gradientResourceAdapter( new KoResourceServerAdapter(serverProvider->gradientServer())); d->gradientAction = new KoResourcePopupAction(gradientResourceAdapter, d->ui->btnChoosePredefinedGradient); d->gradientAction->setToolTip(i18n("Change filling gradient")); d->ui->btnChoosePredefinedGradient->setDefaultAction(d->gradientAction); d->ui->btnChoosePredefinedGradient->setPopupMode(QToolButton::InstantPopup); connect(d->gradientAction, SIGNAL(resourceSelected(QSharedPointer)), SLOT(gradientResourceChanged())); connect(d->ui->btnChoosePredefinedGradient, SIGNAL(iconSizeChanged()), d->gradientAction, SLOT(updateIcon())); d->ui->btnSaveGradient->setIcon(KisIconUtils::loadIcon("document-save")); connect(d->ui->btnSaveGradient, SIGNAL(clicked()), SLOT(slotSavePredefinedGradientClicked())); connect(d->ui->cmbGradientRepeat, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientRepeatChanged())); connect(d->ui->cmbGradientType, SIGNAL(currentIndexChanged(int)), SLOT(slotGradientTypeChanged())); deactivate(); #if 0 // Pattern selector QSharedPointerpatternResourceAdapter(new KoResourceServerAdapter(serverProvider->patternServer())); d->patternAction = new KoResourcePopupAction(patternResourceAdapter, d->colorButton); d->patternAction->setToolTip(i18n("Change the filling pattern")); connect(d->patternAction, SIGNAL(resourceSelected(QSharedPointer)), this, SLOT(patternChanged(QSharedPointer))); connect(d->colorButton, SIGNAL(iconSizeChanged()), d->patternAction, SLOT(updateIcon())); #endif } KoFillConfigWidget::~KoFillConfigWidget() { delete d; } void KoFillConfigWidget::activate() { KIS_SAFE_ASSERT_RECOVER_RETURN(!d->deactivationLocks.empty()); d->deactivationLocks.clear(); if (!d->noSelectionTrackingMode) { d->shapeChangedCompressor.start(); } else { loadCurrentFillFromResourceServer(); } updateWidgetComponentVisbility(); } void KoFillConfigWidget::deactivate() { + emit sigInternalRecoverColorInResourceManager(); + KIS_SAFE_ASSERT_RECOVER_RETURN(d->deactivationLocks.empty()); d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->shapeChangedAcyclicConnector)); d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector)); } void KoFillConfigWidget::forceUpdateOnSelectionChanged() { d->shapeChangedCompressor.start(); } void KoFillConfigWidget::setNoSelectionTrackingMode(bool value) { d->noSelectionTrackingMode = value; if (!d->noSelectionTrackingMode) { d->shapeChangedCompressor.start(); } } void KoFillConfigWidget::slotUpdateFillTitle() { QString text = d->group->checkedButton() ? d->group->checkedButton()->text() : QString(); text.replace('&', QString()); d->ui->lblFillTitle->setText(text); } void KoFillConfigWidget::slotCanvasResourceChanged(int key, const QVariant &value) { if ((key == KoCanvasResourceProvider::ForegroundColor && d->fillVariant == KoFlake::Fill) || (key == KoCanvasResourceProvider::BackgroundColor && d->fillVariant == KoFlake::StrokeFill && !d->noSelectionTrackingMode) || (key == KoCanvasResourceProvider::ForegroundColor && d->noSelectionTrackingMode)) { KoColor color = value.value(); const int checkedId = d->group->checkedId(); if ((checkedId < 0 || checkedId == None || checkedId == Solid) && !(checkedId == Solid && d->colorAction->currentKoColor() == color)) { d->group->button(Solid)->setChecked(true); d->selectedFillIndex = Solid; d->colorAction->setCurrentColor(color); d->colorChangedCompressor.start(); } else if (checkedId == Gradient && key == KoCanvasResourceProvider::ForegroundColor) { d->ui->wdgGradientEditor->notifyGlobalColorChanged(color); } } else if (key == KisCanvasResourceProvider::CurrentGradient) { KoResource *gradient = value.value(); const int checkedId = d->group->checkedId(); if (gradient && (checkedId < 0 || checkedId == None || checkedId == Gradient)) { d->group->button(Gradient)->setChecked(true); d->gradientAction->setCurrentResource(gradient); } } } QList KoFillConfigWidget::currentShapes() { return d->canvas->selectedShapesProxy()->selection()->selectedEditableShapes(); } int KoFillConfigWidget::selectedFillIndex() { return d->selectedFillIndex; } void KoFillConfigWidget::styleButtonPressed(int buttonId) { QList shapes = currentShapes(); switch (buttonId) { case KoFillConfigWidget::None: noColorSelected(); break; case KoFillConfigWidget::Solid: colorChanged(); break; case KoFillConfigWidget::Gradient: if (d->activeGradient) { setNewGradientBackgroundToShape(); updateGradientSaveButtonAvailability(); } else { gradientResourceChanged(); } break; case KoFillConfigWidget::Pattern: // Only select mode in the widget, don't set actual pattern :/ //d->colorButton->setDefaultAction(d->patternAction); //patternChanged(d->patternAction->currentBackground()); break; } // update tool option fields with first selected object if (shapes.isEmpty() == false) { KoShape *firstShape = shapes.first(); updateFillIndexFromShape(firstShape); updateFillColorFromShape(firstShape); } updateWidgetComponentVisbility(); } KoShapeStrokeSP KoFillConfigWidget::createShapeStroke() { KoShapeStrokeSP stroke(new KoShapeStroke()); KIS_ASSERT_RECOVER_RETURN_VALUE(d->fillVariant == KoFlake::StrokeFill, stroke); switch (d->group->checkedId()) { case KoFillConfigWidget::None: stroke->setColor(Qt::transparent); break; case KoFillConfigWidget::Solid: stroke->setColor(d->colorAction->currentColor()); break; case KoFillConfigWidget::Gradient: { QScopedPointer g(d->activeGradient->toQGradient()); QBrush newBrush = *g; stroke->setLineBrush(newBrush); stroke->setColor(Qt::transparent); break; } case KoFillConfigWidget::Pattern: break; } return stroke; } void KoFillConfigWidget::noColorSelected() { KisAcyclicSignalConnector::Blocker b(d->shapeChangedAcyclicConnector); QList selectedShapes = currentShapes(); if (selectedShapes.isEmpty()) { emit sigFillChanged(); return; } KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant); KUndo2Command *command = wrapper.setColor(QColor()); if (command) { d->canvas->addCommand(command); } if (d->fillVariant == KoFlake::StrokeFill) { KUndo2Command *lineCommand = wrapper.setLineWidth(0.0); if (lineCommand) { d->canvas->addCommand(lineCommand); } } emit sigFillChanged(); } void KoFillConfigWidget::colorChanged() { KisAcyclicSignalConnector::Blocker b(d->shapeChangedAcyclicConnector); QList selectedShapes = currentShapes(); if (selectedShapes.isEmpty()) { emit sigInternalRequestColorToResourceManager(); emit sigFillChanged(); return; } + d->overriddenColorFromProvider = boost::none; + KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant); KUndo2Command *command = wrapper.setColor(d->colorAction->currentColor()); if (command) { d->canvas->addCommand(command); } // only returns true if it is a stroke object that has a 0 for line width if (wrapper.hasZeroLineWidth() ) { KUndo2Command *lineCommand = wrapper.setLineWidth(1.0); if (lineCommand) { d->canvas->addCommand(lineCommand); } // * line to test out QColor solidColor = d->colorAction->currentColor(); solidColor.setAlpha(255); command = wrapper.setColor(solidColor); if (command) { d->canvas->addCommand(command); } } d->colorAction->setCurrentColor(wrapper.color()); emit sigFillChanged(); emit sigInternalRequestColorToResourceManager(); } void KoFillConfigWidget::slotProposeCurrentColorToResourceManager() { const int checkedId = d->group->checkedId(); bool hasColor = false; KoColor color; KoCanvasResourceProvider::CanvasResource colorSlot = KoCanvasResourceProvider::ForegroundColor; if (checkedId == Solid) { if (d->fillVariant == KoFlake::StrokeFill) { colorSlot = KoCanvasResourceProvider::BackgroundColor; } color = d->colorAction->currentKoColor(); hasColor = true; } else if (checkedId == Gradient) { if (boost::optional gradientColor = d->ui->wdgGradientEditor->currentActiveStopColor()) { color = *gradientColor; hasColor = true; } } if (hasColor) { + if (!d->overriddenColorFromProvider) { + d->overriddenColorFromProvider = + d->canvas->resourceManager()->resource(colorSlot).value(); + } + /** * Don't let opacity leak to our resource manager system * * NOTE: theoretically, we could guarantee it on a level of the * resource manager itself, */ color.setOpacity(OPACITY_OPAQUE_U8); d->canvas->resourceManager()->setResource(colorSlot, QVariant::fromValue(color)); } } +void KoFillConfigWidget::slotRecoverColorInResourceManager() +{ + if (d->overriddenColorFromProvider) { + KoCanvasResourceProvider::CanvasResource colorSlot = KoCanvasResourceProvider::ForegroundColor; + if (d->fillVariant == KoFlake::StrokeFill) { + colorSlot = KoCanvasResourceProvider::BackgroundColor; + } + + d->canvas->resourceManager()->setResource(colorSlot, QVariant::fromValue(*d->overriddenColorFromProvider)); + d->overriddenColorFromProvider = boost::none; + } +} + template QString findFirstAvailableResourceName(const QString &baseName, ResourceServer *server) { if (!server->resourceByName(baseName)) return baseName; int counter = 1; QString result; while ((result = QString("%1%2").arg(baseName).arg(counter)), server->resourceByName(result)) { counter++; } return result; } void KoFillConfigWidget::slotSavePredefinedGradientClicked() { KoResourceServerProvider *serverProvider = KoResourceServerProvider::instance(); auto server = serverProvider->gradientServer(); const QString defaultGradientNamePrefix = i18nc("default prefix for the saved gradient", "gradient"); QString name = d->activeGradient->name().isEmpty() ? defaultGradientNamePrefix : d->activeGradient->name(); name = findFirstAvailableResourceName(name, server); name = QInputDialog::getText(this, i18nc("@title:window", "Save Gradient"), i18n("Enter gradient name:"), QLineEdit::Normal, name); // TODO: currently we do not allow the user to // create two resources with the same name! // Please add some feedback for it! name = findFirstAvailableResourceName(name, server); d->activeGradient->setName(name); const QString saveLocation = server->saveLocation(); d->activeGradient->setFilename(saveLocation + d->activeGradient->name() + d->activeGradient->defaultFileExtension()); KoAbstractGradient *newGradient = d->activeGradient->clone(); server->addResource(newGradient); d->gradientAction->setCurrentResource(newGradient); } void KoFillConfigWidget::activeGradientChanged() { setNewGradientBackgroundToShape(); updateGradientSaveButtonAvailability(); emit sigInternalRequestColorToResourceManager(); } void KoFillConfigWidget::gradientResourceChanged() { QSharedPointer bg = qSharedPointerDynamicCast( d->gradientAction->currentBackground()); uploadNewGradientBackground(bg->gradient()); setNewGradientBackgroundToShape(); updateGradientSaveButtonAvailability(); } void KoFillConfigWidget::slotGradientTypeChanged() { QGradient::Type type = d->ui->cmbGradientType->currentIndex() == 0 ? QGradient::LinearGradient : QGradient::RadialGradient; d->activeGradient->setType(type); activeGradientChanged(); } void KoFillConfigWidget::slotGradientRepeatChanged() { QGradient::Spread spread = QGradient::Spread(d->ui->cmbGradientRepeat->currentIndex()); d->activeGradient->setSpread(spread); activeGradientChanged(); } void KoFillConfigWidget::uploadNewGradientBackground(const QGradient *gradient) { KisSignalsBlocker b1(d->ui->wdgGradientEditor, d->ui->cmbGradientType, d->ui->cmbGradientRepeat); d->ui->wdgGradientEditor->setGradient(0); d->activeGradient.reset(KoStopGradient::fromQGradient(gradient)); d->ui->wdgGradientEditor->setGradient(d->activeGradient.data()); d->ui->cmbGradientType->setCurrentIndex(d->activeGradient->type() != QGradient::LinearGradient); d->ui->cmbGradientRepeat->setCurrentIndex(int(d->activeGradient->spread())); } void KoFillConfigWidget::setNewGradientBackgroundToShape() { QList selectedShapes = currentShapes(); if (selectedShapes.isEmpty()) { emit sigFillChanged(); return; } KisAcyclicSignalConnector::Blocker b(d->shapeChangedAcyclicConnector); KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant); QScopedPointer srcQGradient(d->activeGradient->toQGradient()); KUndo2Command *command = wrapper.applyGradientStopsOnly(srcQGradient.data()); if (command) { d->canvas->addCommand(command); } emit sigFillChanged(); } void KoFillConfigWidget::updateGradientSaveButtonAvailability() { bool savingEnabled = false; QScopedPointer currentGradient(d->activeGradient->toQGradient()); QSharedPointer bg = d->gradientAction->currentBackground(); if (bg) { QSharedPointer resourceBackground = qSharedPointerDynamicCast(bg); savingEnabled = resourceBackground->gradient()->stops() != currentGradient->stops(); savingEnabled |= resourceBackground->gradient()->type() != currentGradient->type(); savingEnabled |= resourceBackground->gradient()->spread() != currentGradient->spread(); } d->ui->btnSaveGradient->setEnabled(savingEnabled); } void KoFillConfigWidget::patternChanged(QSharedPointer background) { Q_UNUSED(background); #if 0 QSharedPointer patternBackground = qSharedPointerDynamicCast(background); if (! patternBackground) { return; } QList selectedShapes = currentShapes(); if (selectedShapes.isEmpty()) { return; } KoImageCollection *imageCollection = d->canvas->shapeController()->resourceManager()->imageCollection(); if (imageCollection) { QSharedPointer fill(new KoPatternBackground(imageCollection)); fill->setPattern(patternBackground->pattern()); d->canvas->addCommand(new KoShapeBackgroundCommand(selectedShapes, fill)); } #endif } void KoFillConfigWidget::loadCurrentFillFromResourceServer() { { KoColor color = d->canvas->resourceManager()->backgroundColor(); slotCanvasResourceChanged(KoCanvasResourceProvider::BackgroundColor, QVariant::fromValue(color)); } { KoColor color = d->canvas->resourceManager()->foregroundColor(); slotCanvasResourceChanged(KoCanvasResourceProvider::ForegroundColor, QVariant::fromValue(color)); } Q_FOREACH (QAbstractButton *button, d->group->buttons()) { button->setEnabled(true); } emit sigFillChanged(); } void KoFillConfigWidget::shapeChanged() { if (d->noSelectionTrackingMode) return; QList shapes = currentShapes(); - // check to see if the shape actually changed...or is still the same shape - if (d->previousShapeSelected == shapes) { - return; - } else { - d->previousShapeSelected = shapes; - } + bool shouldUploadColorToResourceManager = false; if (shapes.isEmpty() || (shapes.size() > 1 && KoShapeFillWrapper(shapes, d->fillVariant).isMixedFill())) { Q_FOREACH (QAbstractButton *button, d->group->buttons()) { button->setEnabled(!shapes.isEmpty()); } } else { // only one vector object selected Q_FOREACH (QAbstractButton *button, d->group->buttons()) { button->setEnabled(true); } // update active index of shape KoShape *shape = shapes.first(); updateFillIndexFromShape(shape); updateFillColorFromShape(shape); // updates tool options fields + + shouldUploadColorToResourceManager = true; } // updates the UI d->group->button(d->selectedFillIndex)->setChecked(true); updateWidgetComponentVisbility(); slotUpdateFillTitle(); + + if (shouldUploadColorToResourceManager) { + emit sigInternalRequestColorToResourceManager(); + } else { + emit sigInternalRecoverColorInResourceManager(); + } } void KoFillConfigWidget::updateFillIndexFromShape(KoShape *shape) { KIS_SAFE_ASSERT_RECOVER_RETURN(shape); KoShapeFillWrapper wrapper(shape, d->fillVariant); switch (wrapper.type()) { case KoFlake::None: d->selectedFillIndex = KoFillConfigWidget::None; break; case KoFlake::Solid: d->selectedFillIndex = KoFillConfigWidget::Solid; break; case KoFlake::Gradient: d->selectedFillIndex = KoFillConfigWidget::Gradient; break; case KoFlake::Pattern: d->selectedFillIndex = KoFillConfigWidget::Pattern; break; } } void KoFillConfigWidget::updateFillColorFromShape(KoShape *shape) { KIS_SAFE_ASSERT_RECOVER_RETURN(shape); KoShapeFillWrapper wrapper(shape, d->fillVariant); switch (wrapper.type()) { case KoFlake::None: break; case KoFlake::Solid: { QColor color = wrapper.color(); if (color.alpha() > 0) { d->colorAction->setCurrentColor(wrapper.color()); } break; } case KoFlake::Gradient: uploadNewGradientBackground(wrapper.gradient()); updateGradientSaveButtonAvailability(); break; case KoFlake::Pattern: break; } } void KoFillConfigWidget::updateWidgetComponentVisbility() { // The UI is showing/hiding things like this because the 'stacked widget' isn't very flexible // and makes it difficult to put anything underneath it without a lot empty space // hide everything first d->ui->wdgGradientEditor->setVisible(false); d->ui->btnChoosePredefinedGradient->setVisible(false); d->ui->btnChooseSolidColor->setVisible(false); d->ui->typeLabel->setVisible(false); d->ui->repeatLabel->setVisible(false); d->ui->cmbGradientRepeat->setVisible(false); d->ui->cmbGradientType->setVisible(false); d->ui->btnSolidColorPick->setVisible(false); d->ui->btnSaveGradient->setVisible(false); d->ui->gradientTypeLine->setVisible(false); d->ui->soldStrokeColorLabel->setVisible(false); d->ui->presetLabel->setVisible(false); // keep options hidden if no vector shapes are selected if(currentShapes().isEmpty()) { return; } switch (d->selectedFillIndex) { case KoFillConfigWidget::None: break; case KoFillConfigWidget::Solid: d->ui->btnChooseSolidColor->setVisible(true); d->ui->btnSolidColorPick->setVisible(false); d->ui->soldStrokeColorLabel->setVisible(true); break; case KoFillConfigWidget::Gradient: d->ui->wdgGradientEditor->setVisible(true); d->ui->btnChoosePredefinedGradient->setVisible(true); d->ui->typeLabel->setVisible(true); d->ui->repeatLabel->setVisible(true); d->ui->cmbGradientRepeat->setVisible(true); d->ui->cmbGradientType->setVisible(true); d->ui->btnSaveGradient->setVisible(true); d->ui->gradientTypeLine->setVisible(true); d->ui->presetLabel->setVisible(true); break; case KoFillConfigWidget::Pattern: break; } } diff --git a/libs/ui/widgets/KoFillConfigWidget.h b/libs/ui/widgets/KoFillConfigWidget.h index 8c28cce82f..d59dcc66e0 100644 --- a/libs/ui/widgets/KoFillConfigWidget.h +++ b/libs/ui/widgets/KoFillConfigWidget.h @@ -1,124 +1,126 @@ /* This file is part of the KDE project * Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr) * Copyright (C) 2012 Jean-Nicolas Artaud * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef FILLCONFIGWIDGET_H #define FILLCONFIGWIDGET_H #include "kritaui_export.h" #include #include #include #include class KoShapeFillWrapper; class KoCanvasBase; class KoShapeBackground; class KoShape; /// A widget for configuring the fill of a shape class KRITAUI_EXPORT KoFillConfigWidget : public QWidget { Q_OBJECT enum StyleButton { None = 0, Solid, Gradient, Pattern }; public: /** * @param trackShapeSelection controls if the widget connects to the canvas's selectionChanged signal. * If you decide to pass 'false', then don't forget to call * forceUpdateOnSelectionChanged() manually of every selectionChanged() and * selectionContentChanged() signals. */ explicit KoFillConfigWidget(KoCanvasBase *canvas, KoFlake::FillVariant fillVariant, bool trackShapeSelection, QWidget *parent); ~KoFillConfigWidget() override; void setNoSelectionTrackingMode(bool value); /// Returns the list of the selected shape /// If you need to use only one shape, call currentShape() QList currentShapes(); /// returns the selected index of the fill type int selectedFillIndex(); KoShapeStrokeSP createShapeStroke(); void activate(); void deactivate(); void forceUpdateOnSelectionChanged(); private Q_SLOTS: void styleButtonPressed(int buttonId); void noColorSelected(); void shapeChanged(); /// apply color changes to the selected shape void colorChanged(); /// the pattern of the fill changed, apply the changes void patternChanged(QSharedPointer background); void slotUpdateFillTitle(); void slotCanvasResourceChanged(int key, const QVariant &value); void slotSavePredefinedGradientClicked(); void activeGradientChanged(); void gradientResourceChanged(); void slotGradientTypeChanged(); void slotGradientRepeatChanged(); void slotProposeCurrentColorToResourceManager(); + void slotRecoverColorInResourceManager(); Q_SIGNALS: void sigFillChanged(); void sigInternalRequestColorToResourceManager(); + void sigInternalRecoverColorInResourceManager(); private: void uploadNewGradientBackground(const QGradient *gradient); void setNewGradientBackgroundToShape(); void updateGradientSaveButtonAvailability(); void loadCurrentFillFromResourceServer(); void updateWidgetComponentVisbility(); /// update the widget with the KoShape background void updateFillIndexFromShape(KoShape *shape); void updateFillColorFromShape(KoShape *shape); class Private; Private * const d; }; #endif // FILLCONFIGWIDGET_H