diff --git a/libs/flake/KoShapeFillWrapper.cpp b/libs/flake/KoShapeFillWrapper.cpp index 4430eefada..fa0ad8b6e5 100644 --- a/libs/flake/KoShapeFillWrapper.cpp +++ b/libs/flake/KoShapeFillWrapper.cpp @@ -1,370 +1,431 @@ /* * Copyright (c) 2017 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 "KoShapeFillWrapper.h" #include #include #include #include #include #include #include #include #include #include #include "kis_assert.h" #include "kis_debug.h" #include "kis_global.h" #include struct ShapeBackgroundFetchPolicy { typedef KoFlake::FillType Type; typedef QSharedPointer PointerType; static PointerType getBackground(KoShape *shape) { return shape->background(); } static Type type(KoShape *shape) { QSharedPointer background = shape->background(); QSharedPointer colorBackground = qSharedPointerDynamicCast(background); QSharedPointer gradientBackground = qSharedPointerDynamicCast(background); QSharedPointer patternBackground = qSharedPointerDynamicCast(background); - return colorBackground ? Type::Solid : - gradientBackground ? Type::Gradient : - patternBackground ? Type::Pattern : Type::None; + if(gradientBackground) { + return Type::Gradient; + } + + if (patternBackground) { + return Type::Pattern; + } + + if (colorBackground) { + return Type::Solid; + } + + return Type::None; } static QColor color(KoShape *shape) { QSharedPointer colorBackground = qSharedPointerDynamicCast(shape->background()); return colorBackground ? colorBackground->color() : QColor(); } static const QGradient* gradient(KoShape *shape) { QSharedPointer gradientBackground = qSharedPointerDynamicCast(shape->background()); return gradientBackground ? gradientBackground->gradient() : 0; } static QTransform gradientTransform(KoShape *shape) { QSharedPointer gradientBackground = qSharedPointerDynamicCast(shape->background()); return gradientBackground ? gradientBackground->transform() : QTransform(); } static bool compareTo(PointerType p1, PointerType p2) { return p1->compareTo(p2.data()); } }; struct ShapeStrokeFillFetchPolicy { typedef KoFlake::FillType Type; typedef KoShapeStrokeModelSP PointerType; static PointerType getBackground(KoShape *shape) { return shape->stroke(); } static Type type(KoShape *shape) { KoShapeStrokeSP stroke = qSharedPointerDynamicCast(shape->stroke()); if (!stroke) return Type::None; - // TODO: patterns are not supported yet! - return stroke->lineBrush().gradient() ? Type::Gradient : Type::Solid; + // Pattern type not implemented yet, so that logic will have to be added here later + if (stroke->lineBrush().gradient()) { + return Type::Gradient; + } else { + + // strokes without any width are none + if (stroke->lineWidth() == 0.0) { + return Type::None; + } + + return Type::Solid; + } } static QColor color(KoShape *shape) { KoShapeStrokeSP stroke = qSharedPointerDynamicCast(shape->stroke()); return stroke ? stroke->color() : QColor(); } static const QGradient* gradient(KoShape *shape) { KoShapeStrokeSP stroke = qSharedPointerDynamicCast(shape->stroke()); return stroke ? stroke->lineBrush().gradient() : 0; } static QTransform gradientTransform(KoShape *shape) { KoShapeStrokeSP stroke = qSharedPointerDynamicCast(shape->stroke()); return stroke ? stroke->lineBrush().transform() : QTransform(); } static bool compareTo(PointerType p1, PointerType p2) { return p1->compareFillTo(p2.data()); } }; template bool compareBackgrounds(const QList shapes) { if (shapes.size() == 1) return true; typename Policy::PointerType bg = Policy::getBackground(shapes.first()); Q_FOREACH (KoShape *shape, shapes) { if ( !( (!bg && !Policy::getBackground(shape)) || (bg && Policy::compareTo(bg, Policy::getBackground(shape))) )) { return false; } } return true; } /******************************************************************************/ /* KoShapeFillWrapper::Private */ /******************************************************************************/ struct KoShapeFillWrapper::Private { QList shapes; KoFlake::FillVariant fillVariant= KoFlake::Fill; QSharedPointer applyFillGradientStops(KoShape *shape, const QGradient *srcQGradient); void applyFillGradientStops(KoShapeStrokeSP shapeStroke, const QGradient *stopGradient); }; QSharedPointer KoShapeFillWrapper::Private::applyFillGradientStops(KoShape *shape, const QGradient *stopGradient) { QGradientStops stops = stopGradient->stops(); if (!shape || !stops.count()) { return QSharedPointer(); } KoGradientBackground *newGradient = 0; QSharedPointer oldGradient = qSharedPointerDynamicCast(shape->background()); if (oldGradient) { // just copy the gradient and set the new stops QGradient *g = KoFlake::mergeGradient(oldGradient->gradient(), stopGradient); newGradient = new KoGradientBackground(g); newGradient->setTransform(oldGradient->transform()); } else { // No gradient yet, so create a new one. QScopedPointer fakeShapeGradient(new QLinearGradient(QPointF(0, 0), QPointF(1, 1))); fakeShapeGradient->setCoordinateMode(QGradient::ObjectBoundingMode); QGradient *g = KoFlake::mergeGradient(fakeShapeGradient.data(), stopGradient); newGradient = new KoGradientBackground(g); } return QSharedPointer(newGradient); } void KoShapeFillWrapper::Private::applyFillGradientStops(KoShapeStrokeSP shapeStroke, const QGradient *stopGradient) { QGradientStops stops = stopGradient->stops(); if (!stops.count()) return; QLinearGradient fakeShapeGradient(QPointF(0, 0), QPointF(1, 1)); fakeShapeGradient.setCoordinateMode(QGradient::ObjectBoundingMode); QTransform gradientTransform; const QGradient *shapeGradient = 0; { QBrush brush = shapeStroke->lineBrush(); gradientTransform = brush.transform(); shapeGradient = brush.gradient() ? brush.gradient() : &fakeShapeGradient; } { QScopedPointer g(KoFlake::mergeGradient(shapeGradient, stopGradient)); QBrush newBrush = *g; newBrush.setTransform(gradientTransform); shapeStroke->setLineBrush(newBrush); } } /******************************************************************************/ /* KoShapeFillWrapper */ /******************************************************************************/ KoShapeFillWrapper::KoShapeFillWrapper(KoShape *shape, KoFlake::FillVariant fillVariant) : m_d(new Private()) { KIS_SAFE_ASSERT_RECOVER_RETURN(shape); m_d->shapes << shape; m_d->fillVariant= fillVariant; } KoShapeFillWrapper::KoShapeFillWrapper(QList shapes, KoFlake::FillVariant fillVariant) : m_d(new Private()) { KIS_SAFE_ASSERT_RECOVER_RETURN(!shapes.isEmpty()); m_d->shapes = shapes; m_d->fillVariant= fillVariant; } KoShapeFillWrapper::~KoShapeFillWrapper() { } bool KoShapeFillWrapper::isMixedFill() const { if (m_d->shapes.isEmpty()) return false; return m_d->fillVariant == KoFlake::Fill ? !compareBackgrounds(m_d->shapes) : !compareBackgrounds(m_d->shapes); } KoFlake::FillType KoShapeFillWrapper::type() const { if (m_d->shapes.isEmpty() || isMixedFill()) return KoFlake::None; KoShape *shape = m_d->shapes.first(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, KoFlake::None); - return m_d->fillVariant == KoFlake::Fill ? - ShapeBackgroundFetchPolicy::type(shape) : - ShapeStrokeFillFetchPolicy::type(shape); + KoFlake::FillType fillType; + if (m_d->fillVariant == KoFlake::Fill) { + // fill property of vector object + fillType = ShapeBackgroundFetchPolicy::type(shape); + } else { + // stroke property of vector object + fillType = ShapeStrokeFillFetchPolicy::type(shape); + } + + return fillType; } QColor KoShapeFillWrapper::color() const { // this check guarantees that the shapes list is not empty and // the fill is not mixed! if (type() != KoFlake::Solid) return QColor(); KoShape *shape = m_d->shapes.first(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, QColor()); return m_d->fillVariant == KoFlake::Fill ? ShapeBackgroundFetchPolicy::color(shape) : ShapeStrokeFillFetchPolicy::color(shape); } const QGradient* KoShapeFillWrapper::gradient() const { // this check guarantees that the shapes list is not empty and // the fill is not mixed! if (type() != KoFlake::Gradient) return 0; KoShape *shape = m_d->shapes.first(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, 0); return m_d->fillVariant == KoFlake::Fill ? ShapeBackgroundFetchPolicy::gradient(shape) : ShapeStrokeFillFetchPolicy::gradient(shape); } QTransform KoShapeFillWrapper::gradientTransform() const { // this check guarantees that the shapes list is not empty and // the fill is not mixed! if (type() != KoFlake::Gradient) return QTransform(); KoShape *shape = m_d->shapes.first(); KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(shape, QTransform()); return m_d->fillVariant == KoFlake::Fill ? ShapeBackgroundFetchPolicy::gradientTransform(shape) : ShapeStrokeFillFetchPolicy::gradientTransform(shape); } + + KUndo2Command *KoShapeFillWrapper::setColor(const QColor &color) { KUndo2Command *command = 0; if (m_d->fillVariant == KoFlake::Fill) { QSharedPointer bg; if (color.isValid()) { bg = toQShared(new KoColorBackground(color)); } QSharedPointer fill(bg); command = new KoShapeBackgroundCommand(m_d->shapes, fill); } else { command = KoFlake::modifyShapesStrokes(m_d->shapes, [color] (KoShapeStrokeSP stroke) { stroke->setLineBrush(Qt::NoBrush); stroke->setColor(color.isValid() ? color : Qt::transparent); }); } return command; } +KUndo2Command *KoShapeFillWrapper::setLineWidth(const float &lineWidth) +{ + KUndo2Command *command = 0; + + command = KoFlake::modifyShapesStrokes(m_d->shapes, [lineWidth](KoShapeStrokeSP stroke) { + stroke->setColor(Qt::transparent); + stroke->setLineWidth(lineWidth); + + }); + + return command; +} + + +bool KoShapeFillWrapper::hasZeroLineWidth() const +{ + KoShape *shape = m_d->shapes.first(); + if (!shape) return false; + if (m_d->fillVariant == KoFlake::Fill) return false; + + // this check is useful to determine if + KoShapeStrokeSP stroke = qSharedPointerDynamicCast(shape->stroke()); + if (!stroke) return false; + + if ( stroke->lineWidth() == 0.0) { + return true; + } + + return false; +} + + KUndo2Command *KoShapeFillWrapper::setGradient(const QGradient *gradient, const QTransform &transform) { KUndo2Command *command = 0; if (m_d->fillVariant == KoFlake::Fill) { QList> newBackgrounds; foreach (KoShape *shape, m_d->shapes) { Q_UNUSED(shape); KoGradientBackground *newGradient = new KoGradientBackground(KoFlake::cloneGradient(gradient)); newGradient->setTransform(transform); newBackgrounds << toQShared(newGradient); } command = new KoShapeBackgroundCommand(m_d->shapes, newBackgrounds); } else { command = KoFlake::modifyShapesStrokes(m_d->shapes, [gradient, transform] (KoShapeStrokeSP stroke) { QBrush newBrush = *gradient; newBrush.setTransform(transform); stroke->setLineBrush(newBrush); stroke->setColor(Qt::transparent); }); } return command; } KUndo2Command* KoShapeFillWrapper::applyGradient(const QGradient *gradient) { return setGradient(gradient, gradientTransform()); } KUndo2Command* KoShapeFillWrapper::applyGradientStopsOnly(const QGradient *gradient) { KUndo2Command *command = 0; if (m_d->fillVariant == KoFlake::Fill) { QList> newBackgrounds; foreach (KoShape *shape, m_d->shapes) { newBackgrounds << m_d->applyFillGradientStops(shape, gradient); } command = new KoShapeBackgroundCommand(m_d->shapes, newBackgrounds); } else { command = KoFlake::modifyShapesStrokes(m_d->shapes, [this, gradient] (KoShapeStrokeSP stroke) { m_d->applyFillGradientStops(stroke, gradient); }); } return command; } diff --git a/libs/flake/KoShapeFillWrapper.h b/libs/flake/KoShapeFillWrapper.h index e8635d8966..8e4af500cb 100644 --- a/libs/flake/KoShapeFillWrapper.h +++ b/libs/flake/KoShapeFillWrapper.h @@ -1,59 +1,61 @@ /* * Copyright (c) 2017 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 KOSHAPEFILLWRAPPER_H #define KOSHAPEFILLWRAPPER_H #include "kritaflake_export.h" #include #include #include class KUndo2Command; class KoShape; class QColor; class QTransform; class QGradient; class KRITAFLAKE_EXPORT KoShapeFillWrapper { public: KoShapeFillWrapper(KoShape *shape, KoFlake::FillVariant fillVariant); KoShapeFillWrapper(QList shapes, KoFlake::FillVariant fillVariant); ~KoShapeFillWrapper(); bool isMixedFill() const; KoFlake::FillType type() const; QColor color() const; const QGradient* gradient() const; QTransform gradientTransform() const; + bool hasZeroLineWidth() const; KUndo2Command* setColor(const QColor &color); + KUndo2Command* setLineWidth(const float &lineWidth); KUndo2Command* setGradient(const QGradient *gradient, const QTransform &transform); KUndo2Command* applyGradient(const QGradient *gradient); KUndo2Command* applyGradientStopsOnly(const QGradient *gradient); private: struct Private; const QScopedPointer m_d; }; #endif // KOSHAPEFILLWRAPPER_H diff --git a/libs/ui/widgets/KoFillConfigWidget.cpp b/libs/ui/widgets/KoFillConfigWidget.cpp index c4b3d4d5c5..be28e78b2b 100644 --- a/libs/ui/widgets/KoFillConfigWidget.cpp +++ b/libs/ui/widgets/KoFillConfigWidget.cpp @@ -1,850 +1,897 @@ /* 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; 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; }; 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()), - this, SLOT(shapeChanged())); + &d->shapeChangedCompressor, SLOT(start())); d->shapeChangedAcyclicConnector.connectBackwardVoid( d->canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), - this, SLOT(shapeChanged())); + &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())); // 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->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) { - shapeChanged(); + d->shapeChangedCompressor.start(); } else { loadCurrentFillFromResourceServer(); } updateWidgetComponentVisbility(); } void KoFillConfigWidget::deactivate() { 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() { - shapeChanged(); + d->shapeChangedCompressor.start(); } void KoFillConfigWidget::setNoSelectionTrackingMode(bool value) { d->noSelectionTrackingMode = value; if (!d->noSelectionTrackingMode) { - shapeChanged(); + 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) { - activeGradientChanged(); + 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; } - if (buttonId >= None && buttonId <= Pattern) { - d->selectedFillIndex = static_cast(buttonId); - updateWidgetComponentVisbility(); + + // 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; } KoShapeFillWrapper wrapper(selectedShapes, d->fillVariant); - KUndo2Command *command = wrapper.setColor(d->colorAction->currentColor()); + + KUndo2Command *command = wrapper.setColor(d->colorAction->currentColor()); if (command) { d->canvas->addCommand(command); } - emit sigInternalRequestColorToResourceManager(); + // 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) { /** * 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)); } } 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; + } + if (shapes.isEmpty() || (shapes.size() > 1 && KoShapeFillWrapper(shapes, d->fillVariant).isMixedFill())) { Q_FOREACH (QAbstractButton *button, d->group->buttons()) { button->setEnabled(!shapes.isEmpty()); } - - d->group->button(None)->setChecked(true); - d->selectedFillIndex = None; - } 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(); - updateWidget(shape); + updateFillIndexFromShape(shape); + updateFillColorFromShape(shape); // updates tool options fields } + + // updates the UI + d->group->button(d->selectedFillIndex)->setChecked(true); + + updateWidgetComponentVisbility(); + slotUpdateFillTitle(); } -bool KoFillConfigWidget::checkNewFillModeIsSame(const KoShapeFillWrapper &w) const +void KoFillConfigWidget::updateFillIndexFromShape(KoShape *shape) { - bool retval = false; - - switch (w.type()) { - case KoFlake::None: - retval = d->selectedFillIndex == None; - break; - case KoFlake::Solid: - retval = d->selectedFillIndex == Solid && w.color() == d->colorAction->currentColor(); - break; - case KoFlake::Gradient: { - QScopedPointer newGradient(KoStopGradient::fromQGradient(w.gradient())); + KIS_SAFE_ASSERT_RECOVER_RETURN(shape); + KoShapeFillWrapper wrapper(shape, d->fillVariant); - retval = d->selectedFillIndex == Gradient && *newGradient == *d->activeGradient; - break; - } - case KoFlake::Pattern: - // TODO: not implemented - retval = d->selectedFillIndex == Pattern && false; - break; + 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; } - - return retval; } -void KoFillConfigWidget::updateWidget(KoShape *shape) +void KoFillConfigWidget::updateFillColorFromShape(KoShape *shape) { KIS_SAFE_ASSERT_RECOVER_RETURN(shape); - - StyleButton newActiveButton = None; KoShapeFillWrapper wrapper(shape, d->fillVariant); - if (checkNewFillModeIsSame(wrapper)) return; - switch (wrapper.type()) { - case KoFlake::None: - break; - case KoFlake::Solid: { - QColor color = wrapper.color(); - if (color.alpha() > 0) { - newActiveButton = KoFillConfigWidget::Solid; - d->colorAction->setCurrentColor(wrapper.color()); + case KoFlake::None: + break; + case KoFlake::Solid: { + QColor color = wrapper.color(); + if (color.alpha() > 0) { + d->colorAction->setCurrentColor(wrapper.color()); + } + break; } - break; - } - case KoFlake::Gradient: - newActiveButton = KoFillConfigWidget::Gradient; - uploadNewGradientBackground(wrapper.gradient()); - updateGradientSaveButtonAvailability(); - break; - case KoFlake::Pattern: - newActiveButton = KoFillConfigWidget::Pattern; - break; + case KoFlake::Gradient: + uploadNewGradientBackground(wrapper.gradient()); + updateGradientSaveButtonAvailability(); + break; + case KoFlake::Pattern: + break; } - - d->group->button(newActiveButton)->setChecked(true); - - d->selectedFillIndex = newActiveButton; - updateWidgetComponentVisbility(); } 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(true); 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 df140506fc..8c28cce82f 100644 --- a/libs/ui/widgets/KoFillConfigWidget.h +++ b/libs/ui/widgets/KoFillConfigWidget.h @@ -1,123 +1,124 @@ /* 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 shapeChanged(); + void slotUpdateFillTitle(); void slotCanvasResourceChanged(int key, const QVariant &value); void slotSavePredefinedGradientClicked(); void activeGradientChanged(); void gradientResourceChanged(); void slotGradientTypeChanged(); void slotGradientRepeatChanged(); void slotProposeCurrentColorToResourceManager(); Q_SIGNALS: void sigFillChanged(); void sigInternalRequestColorToResourceManager(); private: - /// update the widget with the KoShape background - void updateWidget(KoShape *shape); - void uploadNewGradientBackground(const QGradient *gradient); void setNewGradientBackgroundToShape(); void updateGradientSaveButtonAvailability(); void loadCurrentFillFromResourceServer(); void updateWidgetComponentVisbility(); - bool checkNewFillModeIsSame(const KoShapeFillWrapper &w) const; + /// update the widget with the KoShape background + void updateFillIndexFromShape(KoShape *shape); + + void updateFillColorFromShape(KoShape *shape); class Private; Private * const d; }; #endif // FILLCONFIGWIDGET_H diff --git a/libs/ui/widgets/KoStrokeConfigWidget.cpp b/libs/ui/widgets/KoStrokeConfigWidget.cpp index fdcbe39302..e415c4f6fb 100644 --- a/libs/ui/widgets/KoStrokeConfigWidget.cpp +++ b/libs/ui/widgets/KoStrokeConfigWidget.cpp @@ -1,804 +1,810 @@ /* This file is part of the KDE project * Made by Tomislav Lukman (tomislav.lukman@ck.tel.hr) * Copyright (C) 2002 Tomislav Lukman * Copyright (C) 2002-2003 Rob Buis * Copyright (C) 2005-2006 Tim Beaulen * Copyright (C) 2005-2007 Thomas Zander * Copyright (C) 2005-2006, 2011 Inge Wallin * Copyright (C) 2005-2008 Jan Hambrecht * Copyright (C) 2006 C. Boemann * Copyright (C) 2006 Peter Simonsson * Copyright (C) 2006 Laurent Montel * Copyright (C) 2007,2011 Thorsten Zachmann * Copyright (C) 2011 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. */ // Own #include "KoStrokeConfigWidget.h" // Qt #include #include #include #include #include #include #include // KDE #include // Calligra #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ui_KoStrokeConfigWidget.h" #include #include #include "kis_canvas_resource_provider.h" #include "kis_acyclic_signal_connector.h" +#include // Krita #include "kis_double_parse_unit_spin_box.h" class CapNJoinMenu : public QMenu { public: CapNJoinMenu(QWidget *parent = 0); QSize sizeHint() const override; KisDoubleParseUnitSpinBox *miterLimit; QButtonGroup *capGroup; QButtonGroup *joinGroup; }; CapNJoinMenu::CapNJoinMenu(QWidget *parent) : QMenu(parent) { QGridLayout *mainLayout = new QGridLayout(); mainLayout->setMargin(2); // The cap group capGroup = new QButtonGroup(this); capGroup->setExclusive(true); QToolButton *button = 0; button = new QToolButton(this); button->setIcon(koIcon("stroke-cap-butt")); button->setCheckable(true); button->setToolTip(i18n("Butt cap")); capGroup->addButton(button, Qt::FlatCap); mainLayout->addWidget(button, 2, 0); button = new QToolButton(this); button->setIcon(koIcon("stroke-cap-round")); button->setCheckable(true); button->setToolTip(i18n("Round cap")); capGroup->addButton(button, Qt::RoundCap); mainLayout->addWidget(button, 2, 1); button = new QToolButton(this); button->setIcon(koIcon("stroke-cap-square")); button->setCheckable(true); button->setToolTip(i18n("Square cap")); capGroup->addButton(button, Qt::SquareCap); mainLayout->addWidget(button, 2, 2, Qt::AlignLeft); // The join group joinGroup = new QButtonGroup(this); joinGroup->setExclusive(true); button = new QToolButton(this); button->setIcon(koIcon("stroke-join-miter")); button->setCheckable(true); button->setToolTip(i18n("Miter join")); joinGroup->addButton(button, Qt::MiterJoin); mainLayout->addWidget(button, 3, 0); button = new QToolButton(this); button->setIcon(koIcon("stroke-join-round")); button->setCheckable(true); button->setToolTip(i18n("Round join")); joinGroup->addButton(button, Qt::RoundJoin); mainLayout->addWidget(button, 3, 1); button = new QToolButton(this); button->setIcon(koIcon("stroke-join-bevel")); button->setCheckable(true); button->setToolTip(i18n("Bevel join")); joinGroup->addButton(button, Qt::BevelJoin); mainLayout->addWidget(button, 3, 2, Qt::AlignLeft); // Miter limit // set min/max/step and value in points, then set actual unit miterLimit = new KisDoubleParseUnitSpinBox(this); miterLimit->setMinMaxStep(0.0, 1000.0, 0.5); miterLimit->setDecimals(2); miterLimit->setUnit(KoUnit(KoUnit::Point)); miterLimit->setToolTip(i18n("Miter limit")); mainLayout->addWidget(miterLimit, 4, 0, 1, 3); mainLayout->setSizeConstraint(QLayout::SetMinAndMaxSize); setLayout(mainLayout); } QSize CapNJoinMenu::sizeHint() const { return layout()->sizeHint(); } class Q_DECL_HIDDEN KoStrokeConfigWidget::Private { public: Private() : canvas(0), active(true), allowLocalUnitManagement(true), fillConfigWidget(0), - noSelectionTrackingMode(false) + noSelectionTrackingMode(false), + selectionChangedCompressor(200, KisSignalCompressor::FIRST_ACTIVE) { } KoLineStyleSelector *lineStyle; KisDoubleParseUnitSpinBox *lineWidth; KoMarkerSelector *startMarkerSelector; KoMarkerSelector *midMarkerSelector; KoMarkerSelector *endMarkerSelector; CapNJoinMenu *capNJoinMenu; QWidget *spacer; KoCanvasBase *canvas; bool active; bool allowLocalUnitManagement; KoFillConfigWidget *fillConfigWidget; bool noSelectionTrackingMode; KisAcyclicSignalConnector shapeChangedAcyclicConnector; KisAcyclicSignalConnector resourceManagerAcyclicConnector; + KisSignalCompressor selectionChangedCompressor; + std::vector deactivationLocks; Ui_KoStrokeConfigWidget *ui; }; KoStrokeConfigWidget::KoStrokeConfigWidget(KoCanvasBase *canvas, QWidget * parent) : QWidget(parent) , d(new Private()) { // configure GUI d->ui = new Ui_KoStrokeConfigWidget(); d->ui->setupUi(this); setObjectName("Stroke widget"); { // connect the canvas d->shapeChangedAcyclicConnector.connectBackwardVoid( canvas->selectedShapesProxy(), SIGNAL(selectionChanged()), - this, SLOT(selectionChanged())); + &d->selectionChangedCompressor, SLOT(start())); d->shapeChangedAcyclicConnector.connectBackwardVoid( canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), - this, SLOT(selectionChanged())); + &d->selectionChangedCompressor, SLOT(start())); + + connect(&d->selectionChangedCompressor, SIGNAL(timeout()), this, SLOT(selectionChanged())); d->resourceManagerAcyclicConnector.connectBackwardResourcePair( canvas->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), this, SLOT(canvasResourceChanged(int,QVariant))); d->canvas = canvas; } - { - d->fillConfigWidget = new KoFillConfigWidget(canvas, KoFlake::StrokeFill, false, this); + d->fillConfigWidget = new KoFillConfigWidget(canvas, KoFlake::StrokeFill, true, this); d->fillConfigWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); d->ui->fillConfigWidgetLayout->addWidget(d->fillConfigWidget); connect(d->fillConfigWidget, SIGNAL(sigFillChanged()), SIGNAL(sigStrokeChanged())); } d->ui->thicknessLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter); d->ui->thicknessLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // set min/max/step and value in points, then set actual unit - d->ui->lineWidth->setMinMaxStep(0.0, 1000.0, 0.5); + d->ui->lineWidth->setMinMaxStep(0.5, 1000.0, 0.5); // if someone wants 0, just set to "none" on UI d->ui->lineWidth->setDecimals(2); d->ui->lineWidth->setUnit(KoUnit(KoUnit::Point)); d->ui->lineWidth->setToolTip(i18n("Set line width of actual selection")); d->ui->capNJoinButton->setMinimumHeight(25); d->capNJoinMenu = new CapNJoinMenu(this); d->ui->capNJoinButton->setMenu(d->capNJoinMenu); d->ui->capNJoinButton->setText("..."); d->ui->capNJoinButton->setPopupMode(QToolButton::InstantPopup); { // Line style d->ui->strokeStyleLabel->setText(i18n("Line Style:")); d->ui->strokeStyleLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); d->ui->lineStyle->setToolTip(i18nc("@info:tooltip", "Line style")); d->ui->lineStyle->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); d->ui->lineStyle->setLineStyle(Qt::SolidLine, QVector()); } { QList emptyMarkers; d->startMarkerSelector = new KoMarkerSelector(KoFlake::StartMarker, this); d->startMarkerSelector->setToolTip(i18nc("@info:tooltip", "Start marker")); d->startMarkerSelector->updateMarkers(emptyMarkers); d->startMarkerSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred ); d->ui->markerLayout->addWidget(d->startMarkerSelector); d->midMarkerSelector = new KoMarkerSelector(KoFlake::MidMarker, this); d->midMarkerSelector->setToolTip(i18nc("@info:tooltip", "Node marker")); d->midMarkerSelector->updateMarkers(emptyMarkers); d->midMarkerSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred ); d->ui->markerLayout->addWidget(d->midMarkerSelector); d->endMarkerSelector = new KoMarkerSelector(KoFlake::EndMarker, this); d->endMarkerSelector->setToolTip(i18nc("@info:tooltip", "End marker")); d->endMarkerSelector->updateMarkers(emptyMarkers); d->endMarkerSelector->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred ); d->ui->markerLayout->addWidget(d->endMarkerSelector); } // Spacer d->spacer = new QWidget(); d->spacer->setObjectName("SpecialSpacer"); d->ui->markerLayout->addWidget(d->spacer); connect(d->ui->lineStyle, SIGNAL(currentIndexChanged(int)), this, SLOT(applyDashStyleChanges())); connect(d->ui->lineWidth, SIGNAL(valueChangedPt(qreal)), this, SLOT(applyLineWidthChanges())); connect(d->capNJoinMenu->capGroup, SIGNAL(buttonClicked(int)), this, SLOT(applyJoinCapChanges())); connect(d->capNJoinMenu->joinGroup, SIGNAL(buttonClicked(int)), this, SLOT(applyJoinCapChanges())); connect(d->capNJoinMenu->miterLimit, SIGNAL(valueChangedPt(qreal)), this, SLOT(applyJoinCapChanges())); { // Map the marker signals correctly QSignalMapper *mapper = new QSignalMapper(this); connect(mapper, SIGNAL(mapped(int)), SLOT(applyMarkerChanges(int))); connect(d->startMarkerSelector, SIGNAL(currentIndexChanged(int)), mapper, SLOT(map())); connect(d->midMarkerSelector, SIGNAL(currentIndexChanged(int)), mapper, SLOT(map())); connect(d->endMarkerSelector, SIGNAL(currentIndexChanged(int)), mapper, SLOT(map())); mapper->setMapping(d->startMarkerSelector, KoFlake::StartMarker); mapper->setMapping(d->midMarkerSelector, KoFlake::MidMarker); mapper->setMapping(d->endMarkerSelector, KoFlake::EndMarker); } KoDocumentResourceManager *resourceManager = canvas->shapeController()->resourceManager(); if (resourceManager) { KoMarkerCollection *collection = resourceManager->resource(KoDocumentResourceManager::MarkerCollection).value(); if (collection) { updateMarkers(collection->markers()); } } - selectionChanged(); + d->selectionChangedCompressor.start(); d->fillConfigWidget->activate(); deactivate(); } KoStrokeConfigWidget::~KoStrokeConfigWidget() { delete d; } void KoStrokeConfigWidget::setNoSelectionTrackingMode(bool value) { d->fillConfigWidget->setNoSelectionTrackingMode(value); d->noSelectionTrackingMode = value; if (!d->noSelectionTrackingMode) { - selectionChanged(); + d->selectionChangedCompressor.start(); } } // ---------------------------------------------------------------- // getters and setters Qt::PenStyle KoStrokeConfigWidget::lineStyle() const { return d->ui->lineStyle->lineStyle(); } QVector KoStrokeConfigWidget::lineDashes() const { return d->ui->lineStyle->lineDashes(); } qreal KoStrokeConfigWidget::lineWidth() const { return d->ui->lineWidth->value(); } qreal KoStrokeConfigWidget::miterLimit() const { return d->capNJoinMenu->miterLimit->value(); } KoMarker *KoStrokeConfigWidget::startMarker() const { return d->startMarkerSelector->marker(); } KoMarker *KoStrokeConfigWidget::endMarker() const { return d->endMarkerSelector->marker(); } Qt::PenCapStyle KoStrokeConfigWidget::capStyle() const { return static_cast(d->capNJoinMenu->capGroup->checkedId()); } Qt::PenJoinStyle KoStrokeConfigWidget::joinStyle() const { return static_cast(d->capNJoinMenu->joinGroup->checkedId()); } KoShapeStrokeSP KoStrokeConfigWidget::createShapeStroke() { KoShapeStrokeSP stroke(d->fillConfigWidget->createShapeStroke()); stroke->setLineWidth(lineWidth()); stroke->setCapStyle(capStyle()); stroke->setJoinStyle(joinStyle()); stroke->setMiterLimit(miterLimit()); stroke->setLineStyle(lineStyle(), lineDashes()); return stroke; } // ---------------------------------------------------------------- // Other public functions void KoStrokeConfigWidget::updateStyleControlsAvailability(bool enabled) { d->ui->lineWidth->setEnabled(enabled); d->capNJoinMenu->setEnabled(enabled); d->ui->lineStyle->setEnabled(enabled); d->startMarkerSelector->setEnabled(enabled); d->midMarkerSelector->setEnabled(enabled); d->endMarkerSelector->setEnabled(enabled); } void KoStrokeConfigWidget::setUnit(const KoUnit &unit, KoShape *representativeShape) { if (!d->allowLocalUnitManagement) { return; //the unit management is completely transferred to the unitManagers. } blockChildSignals(true); /** * KoStrokeShape knows nothing about the transformations applied * to the shape, which doesn't prevent the shape to apply them and * display the stroke differently. So just take that into account * and show the user correct values using the multiplier in KoUnit. */ KoUnit newUnit(unit); if (representativeShape) { newUnit.adjustByPixelTransform(representativeShape->absoluteTransformation(0)); } d->ui->lineWidth->setUnit(newUnit); d->capNJoinMenu->miterLimit->setUnit(newUnit); d->ui->lineWidth->setLineStep(1.0); d->capNJoinMenu->miterLimit->setLineStep(1.0); blockChildSignals(false); } void KoStrokeConfigWidget::setUnitManagers(KisSpinBoxUnitManager* managerLineWidth, KisSpinBoxUnitManager *managerMitterLimit) { blockChildSignals(true); d->allowLocalUnitManagement = false; d->ui->lineWidth->setUnitManager(managerLineWidth); d->capNJoinMenu->miterLimit->setUnitManager(managerMitterLimit); blockChildSignals(false); } void KoStrokeConfigWidget::updateMarkers(const QList &markers) { d->startMarkerSelector->updateMarkers(markers); d->midMarkerSelector->updateMarkers(markers); d->endMarkerSelector->updateMarkers(markers); } void KoStrokeConfigWidget::activate() { KIS_SAFE_ASSERT_RECOVER_RETURN(!d->deactivationLocks.empty()); d->deactivationLocks.clear(); d->fillConfigWidget->activate(); if (!d->noSelectionTrackingMode) { - selectionChanged(); + // selectionChanged(); + d->selectionChangedCompressor.start(); } else { loadCurrentStrokeFillFromResourceServer(); } } void KoStrokeConfigWidget::deactivate() { KIS_SAFE_ASSERT_RECOVER_RETURN(d->deactivationLocks.empty()); d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->shapeChangedAcyclicConnector)); d->deactivationLocks.push_back(KisAcyclicSignalConnector::Blocker(d->resourceManagerAcyclicConnector)); d->fillConfigWidget->deactivate(); } void KoStrokeConfigWidget::blockChildSignals(bool block) { d->ui->lineWidth->blockSignals(block); d->capNJoinMenu->capGroup->blockSignals(block); d->capNJoinMenu->joinGroup->blockSignals(block); d->capNJoinMenu->miterLimit->blockSignals(block); d->ui->lineStyle->blockSignals(block); d->startMarkerSelector->blockSignals(block); d->midMarkerSelector->blockSignals(block); d->endMarkerSelector->blockSignals(block); } void KoStrokeConfigWidget::setActive(bool active) { d->active = active; } //------------------------ template auto applyChangeToStrokes(KoCanvasBase *canvas, ModifyFunction modifyFunction) -> decltype(modifyFunction(KoShapeStrokeSP()), void()) { KoSelection *selection = canvas->selectedShapesProxy()->selection(); if (!selection) return; QList shapes = selection->selectedEditableShapes(); KUndo2Command *command = KoFlake::modifyShapesStrokes(shapes, modifyFunction); if (command) { canvas->addCommand(command); } } void KoStrokeConfigWidget::applyDashStyleChanges() { applyChangeToStrokes( d->canvas, [this] (KoShapeStrokeSP stroke) { stroke->setLineStyle(lineStyle(), lineDashes()); }); emit sigStrokeChanged(); } void KoStrokeConfigWidget::applyLineWidthChanges() { applyChangeToStrokes( d->canvas, [this] (KoShapeStrokeSP stroke) { stroke->setLineWidth(lineWidth()); }); emit sigStrokeChanged(); } void KoStrokeConfigWidget::applyJoinCapChanges() { applyChangeToStrokes( d->canvas, [this] (KoShapeStrokeSP stroke) { stroke->setCapStyle(static_cast(d->capNJoinMenu->capGroup->checkedId())); stroke->setJoinStyle(static_cast(d->capNJoinMenu->joinGroup->checkedId())); stroke->setMiterLimit(miterLimit()); }); emit sigStrokeChanged(); } void KoStrokeConfigWidget::applyMarkerChanges(int rawPosition) { KoSelection *selection = d->canvas->selectedShapesProxy()->selection(); if (!selection) { emit sigStrokeChanged(); return; } QList shapes = selection->selectedEditableShapes(); QList pathShapes; Q_FOREACH (KoShape *shape, shapes) { KoPathShape *pathShape = dynamic_cast(shape); if (pathShape) { pathShapes << pathShape; } } if (pathShapes.isEmpty()) { emit sigStrokeChanged(); return; } KoFlake::MarkerPosition position = KoFlake::MarkerPosition(rawPosition); QScopedPointer marker; switch (position) { case KoFlake::StartMarker: if (d->startMarkerSelector->marker()) { marker.reset(new KoMarker(*d->startMarkerSelector->marker())); } break; case KoFlake::MidMarker: if (d->midMarkerSelector->marker()) { marker.reset(new KoMarker(*d->midMarkerSelector->marker())); } break; case KoFlake::EndMarker: if (d->endMarkerSelector->marker()) { marker.reset(new KoMarker(*d->endMarkerSelector->marker())); } break; } KUndo2Command* command = new KoPathShapeMarkerCommand(pathShapes, marker.take(), position); d->canvas->addCommand(command); emit sigStrokeChanged(); } // ---------------------------------------------------------------- struct CheckShapeStrokeStyleBasePolicy { typedef KoShapeStrokeSP PointerType; static PointerType getProperty(KoShape *shape) { return qSharedPointerDynamicCast(shape->stroke()); } }; struct CheckShapeStrokeDashesPolicy : public CheckShapeStrokeStyleBasePolicy { static bool compareTo(PointerType p1, PointerType p2) { return p1->lineStyle() == p2->lineStyle() && p1->lineDashes() == p2->lineDashes() && p1->dashOffset() == p2->dashOffset(); } }; struct CheckShapeStrokeCapJoinPolicy : public CheckShapeStrokeStyleBasePolicy { static bool compareTo(PointerType p1, PointerType p2) { return p1->capStyle() == p2->capStyle() && p1->joinStyle() == p2->joinStyle() && p1->miterLimit() == p2->miterLimit(); } }; struct CheckShapeStrokeWidthPolicy : public CheckShapeStrokeStyleBasePolicy { static bool compareTo(PointerType p1, PointerType p2) { return p1->lineWidth() == p2->lineWidth(); } }; struct CheckShapeMarkerPolicy { CheckShapeMarkerPolicy(KoFlake::MarkerPosition position) : m_position(position) { } typedef KoMarker* PointerType; PointerType getProperty(KoShape *shape) const { KoPathShape *pathShape = dynamic_cast(shape); return pathShape ? pathShape->marker(m_position) : 0; } bool compareTo(PointerType p1, PointerType p2) const { if ((!p1 || !p2) && p1 != p2) return false; if (!p1 && p1 == p2) return true; return p1 == p2 || *p1 == *p2; } KoFlake::MarkerPosition m_position; }; void KoStrokeConfigWidget::selectionChanged() { if (d->noSelectionTrackingMode) return; KoSelection *selection = d->canvas->selectedShapesProxy()->selection(); if (!selection) return; // we need to linearize update order, and force the child widget to update // before we start doing it - d->fillConfigWidget->forceUpdateOnSelectionChanged(); - QList shapes = selection->selectedEditableShapes(); + d->fillConfigWidget->forceUpdateOnSelectionChanged(); // calls shapeChanged() logic + KoShape *shape = !shapes.isEmpty() ? shapes.first() : 0; const KoShapeStrokeSP stroke = shape ? qSharedPointerDynamicCast(shape->stroke()) : KoShapeStrokeSP(); // setUnit uses blockChildSignals() so take care not to use it inside the block setUnit(d->canvas->unit(), shape); blockChildSignals(true); // line width if (stroke && KoFlake::compareShapePropertiesEqual(shapes)) { d->ui->lineWidth->changeValue(stroke->lineWidth()); } else { d->ui->lineWidth->changeValue(0); } // caps & joins if (stroke && KoFlake::compareShapePropertiesEqual(shapes)) { Qt::PenCapStyle capStyle = stroke->capStyle() >= 0 ? stroke->capStyle() : Qt::FlatCap; Qt::PenJoinStyle joinStyle = stroke->joinStyle() >= 0 ? stroke->joinStyle() : Qt::MiterJoin; { QAbstractButton *button = d->capNJoinMenu->capGroup->button(capStyle); KIS_SAFE_ASSERT_RECOVER_RETURN(button); button->setChecked(true); } { QAbstractButton *button = d->capNJoinMenu->joinGroup->button(joinStyle); KIS_SAFE_ASSERT_RECOVER_RETURN(button); button->setChecked(true); } d->capNJoinMenu->miterLimit->changeValue(stroke->miterLimit()); d->capNJoinMenu->miterLimit->setEnabled(joinStyle == Qt::MiterJoin); } else { d->capNJoinMenu->capGroup->button(Qt::FlatCap)->setChecked(true); d->capNJoinMenu->joinGroup->button(Qt::MiterJoin)->setChecked(true); d->capNJoinMenu->miterLimit->changeValue(0.0); d->capNJoinMenu->miterLimit->setEnabled(true); } // dashes style if (stroke && KoFlake::compareShapePropertiesEqual(shapes)) { d->ui->lineStyle->setLineStyle(stroke->lineStyle(), stroke->lineDashes()); } else { d->ui->lineStyle->setLineStyle(Qt::SolidLine, QVector()); } // markers KoPathShape *pathShape = dynamic_cast(shape); if (pathShape) { if (KoFlake::compareShapePropertiesEqual(shapes, CheckShapeMarkerPolicy(KoFlake::StartMarker))) { d->startMarkerSelector->setMarker(pathShape->marker(KoFlake::StartMarker)); } if (KoFlake::compareShapePropertiesEqual(shapes, CheckShapeMarkerPolicy(KoFlake::MidMarker))) { d->midMarkerSelector->setMarker(pathShape->marker(KoFlake::MidMarker)); } if (KoFlake::compareShapePropertiesEqual(shapes, CheckShapeMarkerPolicy(KoFlake::EndMarker))) { d->endMarkerSelector->setMarker(pathShape->marker(KoFlake::EndMarker)); } } const bool lineOptionsVisible = d->fillConfigWidget->selectedFillIndex() != 0; // This switch statement is to help the tab widget "pages" to be closer to the correct size // if we don't do this the internal widgets get rendered, then the tab page has to get resized to // fill up the space, then the internal widgets have to resize yet again...causing flicker switch(d->fillConfigWidget->selectedFillIndex()) { case 0: // no fill this->setMinimumHeight(130); break; case 1: // solid fill this->setMinimumHeight(200); break; case 2: // gradient fill this->setMinimumHeight(350); case 3: // pattern fill break; } d->ui->thicknessLineBreak->setVisible(lineOptionsVisible); d->ui->lineWidth->setVisible(lineOptionsVisible); d->ui->capNJoinButton->setVisible(lineOptionsVisible); d->ui->lineStyle->setVisible(lineOptionsVisible); d->startMarkerSelector->setVisible(lineOptionsVisible); d->midMarkerSelector->setVisible(lineOptionsVisible); d->endMarkerSelector->setVisible(lineOptionsVisible); d->ui->thicknessLabel->setVisible(lineOptionsVisible); d->ui->strokeStyleLabel->setVisible(lineOptionsVisible); blockChildSignals(false); updateStyleControlsAvailability(!shapes.isEmpty()); } void KoStrokeConfigWidget::canvasResourceChanged(int key, const QVariant &value) { switch (key) { case KoCanvasResourceProvider::Unit: // we request the whole selection to reload because the // unit of the stroke width depends on the selected shape - selectionChanged(); + d->selectionChangedCompressor.start(); break; case KisCanvasResourceProvider::Size: if (d->noSelectionTrackingMode) { d->ui->lineWidth->changeValue(d->canvas->unit().fromUserValue(value.toReal())); } break; } } void KoStrokeConfigWidget::loadCurrentStrokeFillFromResourceServer() { if (d->canvas) { const QVariant value = d->canvas->resourceManager()->resource(KisCanvasResourceProvider::Size); canvasResourceChanged(KisCanvasResourceProvider::Size, value); updateStyleControlsAvailability(true); emit sigStrokeChanged(); } }