diff --git a/libs/ui/kis_zoom_manager.h b/libs/ui/kis_zoom_manager.h index cb2e93c3d2..ef05b7d830 100644 --- a/libs/ui/kis_zoom_manager.h +++ b/libs/ui/kis_zoom_manager.h @@ -1,112 +1,112 @@ /* * Copyright (C) 2006 Boudewijn Rempt * * 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_ZOOM_MANAGER #define KIS_ZOOM_MANAGER #include #include #include #include #include #include #include #include "kis_signal_auto_connection.h" #include "KisView.h" class KoZoomHandler; class KoZoomAction; class KoRuler; class KoUnit; class KoCanvasController; class QPoint; #include "kritaui_export.h" /** * The zoom manager handles all user actions related to zooming * and unzooming. The actual computation of zoom levels and things * are the job of KoZoomHandler or its descendants */ class KRITAUI_EXPORT KisZoomManager : public QObject { Q_OBJECT public: KisZoomManager(QPointer view, KoZoomHandler*, KoCanvasController *); ~KisZoomManager() override; void updateScreenResolution(QWidget *parentWidget); void setup(KActionCollection * actionCollection); void updateGUI(); KoZoomController * zoomController() const { return m_zoomController; } void updateImageBoundsSnapping(); QWidget *zoomActionWidget() const; KoRuler *horizontalRuler() const; KoRuler *verticalRuler() const; qreal zoom() const; public Q_SLOTS: void slotZoomChanged(KoZoomMode::Mode mode, qreal zoom); void slotScrollAreaSizeChanged(); void setShowRulers(bool show); void setRulersTrackMouse(bool value); void mousePositionChanged(const QPoint &viewPos); void changeAspectMode(bool aspectMode); void pageOffsetChanged(); void zoomTo100(); void applyRulersUnit(const KoUnit &baseUnit); void setMinMaxZoom(); void setRulersPixelMultiple2(bool enabled); private: void updateMouseTrackingConnections(); private: QPointer m_view; KoZoomHandler * m_zoomHandler; KoCanvasController *m_canvasController; KoZoomController *m_zoomController; KoRuler * m_horizontalRuler; KoRuler * m_verticalRuler; KoZoomAction * m_zoomAction; QPointer m_zoomActionWidget; QPoint m_rulersOffset; KisSignalAutoConnectionsStore m_mouseTrackingConnections; qreal m_physicalDpiX; qreal m_physicalDpiY; qreal m_devicePixelRatio; - bool m_aspectMode; + bool m_aspectMode {false}; }; #endif diff --git a/libs/ui/widgets/KoFillConfigWidget.cpp b/libs/ui/widgets/KoFillConfigWidget.cpp index 1d26e890c6..75323e75db 100644 --- a/libs/ui/widgets/KoFillConfigWidget.cpp +++ b/libs/ui/widgets/KoFillConfigWidget.cpp @@ -1,898 +1,898 @@ /* 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::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; }; 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())); // 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() { 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; } 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) { /** * 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()); } } 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 } // updates the UI d->group->button(d->selectedFillIndex)->setChecked(true); updateWidgetComponentVisbility(); slotUpdateFillTitle(); } 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(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/KoStrokeConfigWidget.cpp b/libs/ui/widgets/KoStrokeConfigWidget.cpp index e415c4f6fb..c96e50fd27 100644 --- a/libs/ui/widgets/KoStrokeConfigWidget.cpp +++ b/libs/ui/widgets/KoStrokeConfigWidget.cpp @@ -1,810 +1,809 @@ /* 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), 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; + bool noSelectionTrackingMode {false}; 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()), &d->selectionChangedCompressor, SLOT(start())); d->shapeChangedAcyclicConnector.connectBackwardVoid( canvas->selectedShapesProxy(), SIGNAL(selectionContentChanged()), &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, 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.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()); } } 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) { 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(); 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 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; + 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 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(); } } diff --git a/plugins/dockers/artisticcolorselector/kis_color_selector.cpp b/plugins/dockers/artisticcolorselector/kis_color_selector.cpp index 5427d80fc5..e83e79f8fb 100644 --- a/plugins/dockers/artisticcolorselector/kis_color_selector.cpp +++ b/plugins/dockers/artisticcolorselector/kis_color_selector.cpp @@ -1,1185 +1,1186 @@ /* Copyright (C) 2011 Silvio Heinrich 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_color_selector.h" //#define DEBUG_ARC_SELECTOR KisColorSelector::KisColorSelector(QWidget* parent, KisColor::Type type) : QWidget(parent) , m_colorConverter(KisDisplayColorConverter::dumbConverterInstance()) , m_colorSpace(type) , m_inverseSaturation(false) , m_selectedColor(m_colorConverter) , m_fgColor(m_colorConverter) , m_bgColor(m_colorConverter) , m_clickedRing(-1) , m_gamutMaskOn(false) , m_currentGamutMask(nullptr) , m_maskPreviewActive(true) , m_widgetUpdatesSelf(false) , m_isDirtyWheel(false) , m_isDirtyLightStrip(false) , m_isDirtyGamutMask(false) , m_isDirtyColorPreview(false) { m_viewConverter = new KisGamutMaskViewConverter(); setLumaCoefficients(DEFAULT_LUMA_R, DEFAULT_LUMA_G, DEFAULT_LUMA_B,DEFAULT_LUMA_GAMMA); recalculateRings(DEFAULT_SATURATION_STEPS, DEFAULT_HUE_STEPS); recalculateAreas(DEFAULT_VALUE_SCALE_STEPS); selectColor(KisColor(Qt::red, m_colorConverter, KisColor::HSY, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma)); using namespace std::placeholders; // For _1 placeholder auto function = std::bind(&KisColorSelector::slotUpdateColorAndPreview, this, _1); m_updateColorCompressor.reset(new ColorCompressorType(20 /* ms */, function)); } void KisColorSelector::setColorSpace(KisColor::Type type) { m_colorSpace = type; m_selectedColor = KisColor(m_selectedColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); m_isDirtyLightStrip = true; m_isDirtyWheel = true; #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::setColorSpace: set to:" << m_colorSpace; #endif update(); } void KisColorSelector::setColorConverter(KisDisplayColorConverter *colorConverter) { m_colorConverter = colorConverter; m_selectedColor = KisColor(m_selectedColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); m_fgColor = KisColor(m_fgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); m_bgColor = KisColor(m_bgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); update(); } void KisColorSelector::setNumLightPieces(int num) { num = qBound(MIN_NUM_LIGHT_PIECES, num, MAX_NUM_LIGHT_PIECES); recalculateAreas(quint8(num)); if (m_selectedLightPiece >= 0) m_selectedLightPiece = getLightIndex(m_selectedColor.getX()); update(); } void KisColorSelector::setNumPieces(int num) { num = qBound(MIN_NUM_HUE_PIECES, num, MAX_NUM_HUE_PIECES); recalculateRings(quint8(getNumRings()), quint8(num)); if (m_selectedPiece >= 0) m_selectedPiece = getHueIndex(m_selectedColor.getH() * PI2); update(); } void KisColorSelector::setNumRings(int num) { num = qBound(MIN_NUM_SATURATION_RINGS, num, MAX_NUM_SATURATION_RINGS); recalculateRings(quint8(num), quint8(getNumPieces())); if (m_selectedRing >= 0) m_selectedRing = getSaturationIndex(m_selectedColor.getS()); update(); } void KisColorSelector::selectColor(const KisColor& color) { m_selectedColor = KisColor(color, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); m_selectedPiece = getHueIndex(m_selectedColor.getH() * PI2); m_selectedRing = getSaturationIndex(m_selectedColor.getS()); m_selectedLightPiece = getLightIndex(m_selectedColor.getX()); update(); } void KisColorSelector::setFgColor(const KoColor& fgColor) { if (!m_widgetUpdatesSelf) { m_fgColor = KisColor(fgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); m_selectedColor = KisColor(fgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); m_isDirtyWheel = true; m_isDirtyLightStrip = true; m_isDirtyColorPreview = true; #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::setFgColor: m_fgColor set to:" << "H:" << m_fgColor.getH() << "S:" << m_fgColor.getS() << "X:" << m_fgColor.getX(); dbgPlugins << "KisColorSelector::setFgColor: m_selectedColor set to:" << "H:" << m_selectedColor.getH() << "S:" << m_selectedColor.getS() << "X:" << m_selectedColor.getX(); #endif update(); } } void KisColorSelector::setBgColor(const KoColor& bgColor) { if (!m_widgetUpdatesSelf) { m_bgColor = KisColor(bgColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); m_isDirtyColorPreview = true; #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::setBgColor: m_bgColor set to:" << "H:" << m_bgColor.getH() << "S:" << m_bgColor.getS() << "X:" << m_bgColor.getX(); #endif update(); } } void KisColorSelector::setLight(qreal light) { m_selectedColor.setX(qBound(0.0, light, 1.0)); m_selectedLightPiece = getLightIndex(m_selectedColor.getX()); m_isDirtyLightStrip = true; update(); } void KisColorSelector::setLumaCoefficients(qreal lR, qreal lG, qreal lB, qreal lGamma) { m_lumaR = lR; m_lumaG = lG; m_lumaB = lB; m_lumaGamma = lGamma; m_selectedColor = KisColor(m_selectedColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); m_isDirtyLightStrip = true; m_isDirtyWheel = true; #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::setLumaCoefficients: " << m_lumaR << " " << m_lumaG << " " << m_lumaB << " " << m_lumaGamma; #endif update(); } void KisColorSelector::setInverseSaturation(bool inverse) { if (m_inverseSaturation != inverse) { m_selectedRing = (getNumRings()-1) - m_selectedRing; m_inverseSaturation = inverse; recalculateRings(quint8(getNumRings()), quint8(getNumPieces())); update(); } } void KisColorSelector::setGamutMask(KoGamutMask* gamutMask) { if (!gamutMask) { return; } m_currentGamutMask = gamutMask; m_viewConverter->setViewSize(m_renderAreaSize); m_viewConverter->setMaskSize(m_currentGamutMask->maskSize()); if (m_enforceGamutMask) { m_isDirtyWheel = true; } else { m_isDirtyGamutMask = true; } update(); } void KisColorSelector::setDirty() { m_isDirtyWheel = true; m_isDirtyLightStrip = true; m_isDirtyGamutMask = true; m_isDirtyColorPreview = true; update(); } KoGamutMask* KisColorSelector::gamutMask() { return m_currentGamutMask; } bool KisColorSelector::gamutMaskOn() { return m_gamutMaskOn; } void KisColorSelector::setGamutMaskOn(bool gamutMaskOn) { if (m_currentGamutMask) { m_gamutMaskOn = gamutMaskOn; if (m_enforceGamutMask) { m_isDirtyWheel = true; } else { m_isDirtyGamutMask = true; } update(); } } void KisColorSelector::setEnforceGamutMask(bool enforce) { m_enforceGamutMask = enforce; m_isDirtyGamutMask = true; m_isDirtyWheel = true; update(); } QPointF KisColorSelector::mapCoordToView(const QPointF& pt, const QRectF& viewRect) const { qreal w = viewRect.width() / 2.0; qreal h = viewRect.height() / 2.0; qreal x = pt.x() + 1.0; qreal y = (pt.y()) + 1.0; return QPointF(x*w, y*h); } QPointF KisColorSelector::mapCoordToUnit(const QPointF& pt, const QRectF& viewRect) const { qreal w = viewRect.width() / 2.0; qreal h = viewRect.height() / 2.0; qreal x = pt.x() - (viewRect.x() + w); qreal y = pt.y() - (viewRect.y() + h); return QPointF(x/w, y/h); } QPointF KisColorSelector::mapColorToUnit(const KisColor& color, bool invertSaturation) const { qreal radius; if (invertSaturation && m_inverseSaturation) { radius = 1.0 - color.getS(); } else { radius = color.getS(); } QPointF hueCoord = mapHueToAngle(color.getH()); qreal x = hueCoord.x()*radius; qreal y = hueCoord.y()*radius; return QPointF(x,y); } KisColorSelector::Radian KisColorSelector::mapCoordToAngle(qreal x, qreal y) const { qreal angle = std::atan2(-y, -x); #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::mapCoordToAngle: " << "X:" << x << "Y:" << y << "angle:" << angle; #endif return angle; } QPointF KisColorSelector::mapHueToAngle(qreal hue) const { qreal angle = hue * 2.0 * M_PI - M_PI; qreal x = std::cos(angle); qreal y = std::sin(angle); return QPointF(x,y); } qint8 KisColorSelector::getLightIndex(const QPointF& pt) const { if (m_lightStripArea.contains(pt.toPoint(), true)) { qreal t = (pt.x() - m_lightStripArea.x()) / qreal(m_lightStripArea.width()); t = (pt.y() - m_lightStripArea.y()) / qreal(m_lightStripArea.height()); return qint8(t * getNumLightPieces()); } return -1; } qint8 KisColorSelector::getLightIndex(qreal light) const { light = qreal(1) - qBound(qreal(0), light, qreal(1)); return qint8(qRound(light * (getNumLightPieces()-1))); } qreal KisColorSelector::getLight(const QPointF& pt) const { qint8 clickedLightPiece = getLightIndex(pt); if (clickedLightPiece >= 0) { if (getNumLightPieces() > 1) { return 1.0 - (qreal(clickedLightPiece) / qreal(getNumLightPieces()-1)); } return 1.0 - (qreal(pt.y()) / qreal(m_lightStripArea.height())); } return qreal(0); } qint8 KisColorSelector::getHueIndex(Radian hue) const { qreal partSize = 1.0 / qreal(getNumPieces()); return qint8(qRound(hue.scaled(0.0, 1.0) / partSize) % getNumPieces()); } qreal KisColorSelector::getHue(int hueIdx, Radian shift) const { Radian hue = (qreal(hueIdx) / qreal(getNumPieces())) * PI2; hue += shift; return hue.scaled(0.0, 1.0); } qint8 KisColorSelector::getSaturationIndex(qreal saturation) const { saturation = qBound(qreal(0), saturation, qreal(1)); saturation = m_inverseSaturation ? (qreal(1) - saturation) : saturation; return qint8(saturation * qreal(getNumRings() - 1)); } qint8 KisColorSelector::getSaturationIndex(const QPointF& pt) const { qreal length = std::sqrt(pt.x()*pt.x() + pt.y()*pt.y()); for(int i=0; i= m_colorRings[i].innerRadius && length < m_colorRings[i].outerRadius) return qint8(i); } return -1; } qreal KisColorSelector::getSaturation(int saturationIdx) const { qreal sat = qreal(saturationIdx) / qreal(getNumRings()-1); return m_inverseSaturation ? (1.0 - sat) : sat; } void KisColorSelector::recalculateAreas(quint8 numLightPieces) { qreal LIGHT_STRIP_RATIO = 0.075; + if (m_showValueScaleNumbers) { LIGHT_STRIP_RATIO = 0.25; } int width = QWidget::width(); int height = QWidget::height(); int size = qMin(width, height); int stripThick = int(size * LIGHT_STRIP_RATIO); width -= stripThick; size = qMin(width, height); int x = (width - size) / 2; int y = (height - size) / 2; m_renderAreaSize = QSize(size,size); m_viewConverter->setViewSize(m_renderAreaSize); m_widgetArea = QRect(0, 0, QWidget::width(), QWidget::height()); m_renderArea = QRect(x+stripThick, y, size, size); m_lightStripArea = QRect(0, 0, stripThick, QWidget::height()); m_renderBuffer = QImage(size, size, QImage::Format_ARGB32_Premultiplied); m_colorPreviewBuffer = QImage(QWidget::width(), QWidget::height(), QImage::Format_ARGB32_Premultiplied); m_maskBuffer = QImage(size, size, QImage::Format_ARGB32_Premultiplied); m_lightStripBuffer = QImage(stripThick, QWidget::height(), QImage::Format_ARGB32_Premultiplied); m_numLightPieces = numLightPieces; m_isDirtyGamutMask = true; m_isDirtyLightStrip = true; m_isDirtyWheel = true; m_isDirtyColorPreview = true; } void KisColorSelector::recalculateRings(quint8 numRings, quint8 numPieces) { m_colorRings.resize(numRings); m_numPieces = numPieces; for(int i=0; i(numPieces, 1); ring.innerRadius = innerRadius; ring.outerRadius = outerRadius; ring.pieced.resize(numParts); qreal partSize = 360.0 / qreal(numParts); QRectF outerRect(-outerRadius, -outerRadius, outerRadius*2.0, outerRadius*2.0); QRectF innerRect(-innerRadius, -innerRadius, innerRadius*2.0, innerRadius*2.0); for(int i=0; icoordIsClear(colorCoord, *m_viewConverter, m_maskPreviewActive); if (isClear) { return true; } else { return false; } } else { return true; } return false; } void KisColorSelector::requestUpdateColorAndPreview(const KisColor &color, Acs::ColorRole role) { #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::requestUpdateColorAndPreview: requesting update to: " << "H:" << color.getH() << "S:" << color.getS() << "X:" << color.getX(); #endif m_updateColorCompressor->start(qMakePair(color, role)); } void KisColorSelector::slotUpdateColorAndPreview(QPair color) { const bool selectAsFgColor = color.second == Acs::Foreground; if (selectAsFgColor) { m_fgColor = KisColor(color.first, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); } else { m_bgColor = KisColor(color.first, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); } m_selectedColor = KisColor(color.first, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); m_isDirtyLightStrip = true; m_isDirtyColorPreview = true; m_isDirtyWheel = true; #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::slotUpdateColorAndPreview: m_selectedColor set to:" << "H:" << m_selectedColor.getH() << "S:" << m_selectedColor.getS() << "X:" << m_selectedColor.getX(); #endif if (selectAsFgColor) { emit sigFgColorChanged(m_selectedColor); } else { emit sigBgColorChanged(m_selectedColor); } } void KisColorSelector::drawRing(QPainter& painter, KisColorSelector::ColorRing& ring, const QRect& rect) { painter.save(); painter.setRenderHint(QPainter::Antialiasing, true); painter.resetTransform(); painter.translate(rect.width()/2, rect.height()/2); if (ring.pieced.size() > 1) { QTransform mirror; mirror.rotate(180, Qt::YAxis); painter.setTransform(mirror, true); painter.scale(rect.width()/2, rect.height()/2); QPen normalPen = QPen(QBrush(COLOR_NORMAL_OUTLINE), 0.005); QPen clearMaskPen = QPen(QBrush(COLOR_MASK_CLEAR), 0.005); QBrush brush(Qt::SolidPattern); for(int i=0; i= 1.0) ? (hue - 1.0) : hue; hue = (hue < 0.0) ? (hue + 1.0) : hue; KisColor color(hue, m_colorConverter, m_colorSpace); color.setS(ring.saturation); color.setX(m_selectedColor.getX()); if(m_gamutMaskOn && m_enforceGamutMask && colorIsClear(color)) { painter.setPen(clearMaskPen); } else { painter.setPen(normalPen); } if ((m_enforceGamutMask) && (!colorIsClear(color))) { brush.setColor(COLOR_MASK_FILL); } else { brush.setColor(color.toQColor()); } painter.setBrush(brush); painter.drawPath(ring.pieced[i]); } } else { KisColor colors[7] = { KisColor(Qt::cyan , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma), KisColor(Qt::green , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma), KisColor(Qt::yellow , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma), KisColor(Qt::red , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma), KisColor(Qt::magenta, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma), KisColor(Qt::blue , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma), KisColor(Qt::cyan , m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma) }; QConicalGradient gradient(0, 0, 0); for(int i=0; i<=6; ++i) { qreal hue = qreal(i) / 6.0; colors[i].setS(ring.saturation); colors[i].setX(m_selectedColor.getX()); gradient.setColorAt(hue, colors[i].toQColor()); } painter.scale(rect.width()/2, rect.height()/2); painter.fillPath(ring.pieced[0], QBrush(gradient)); } painter.restore(); } void KisColorSelector::drawOutline(QPainter& painter, const QRect& rect) { painter.save(); painter.setRenderHint(QPainter::Antialiasing, true); painter.resetTransform(); painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); painter.scale(rect.width()/2, rect.height()/2); QPen normalPen = QPen(QBrush(COLOR_NORMAL_OUTLINE), 0.005); QPen selectedPen; painter.setPen(normalPen); if (getNumPieces() > 1) { if (m_selectedRing >= 0 && m_selectedPiece >= 0) { painter.resetTransform(); painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); QTransform mirror; mirror.rotate(180, Qt::YAxis); painter.setTransform(mirror, true); painter.scale(rect.width()/2, rect.height()/2); if (m_selectedColor.getX() < 0.55) { selectedPen = QPen(QBrush(COLOR_SELECTED_LIGHT), 0.007); } else { selectedPen = QPen(QBrush(COLOR_SELECTED_DARK), 0.007); } painter.setPen(selectedPen); painter.drawPath(m_colorRings[m_selectedRing].pieced[m_selectedPiece]); } } else { for(int i=0; i= 0) { qreal iRad = m_colorRings[m_selectedRing].innerRadius; qreal oRad = m_colorRings[m_selectedRing].outerRadius; if (m_selectedColor.getX() < 0.55) { selectedPen = QPen(QBrush(COLOR_SELECTED_LIGHT), 0.005); } else { selectedPen = QPen(QBrush(COLOR_SELECTED_DARK), 0.005); } painter.setPen(selectedPen); painter.drawEllipse(QRectF(-iRad, -iRad, iRad*2.0, iRad*2.0)); painter.drawEllipse(QRectF(-oRad, -oRad, oRad*2.0, oRad*2.0)); QPointF lineCoords = mapHueToAngle(m_selectedColor.getH()); painter.drawLine(QPointF(lineCoords.x()*iRad, lineCoords.y()*iRad), QPointF(lineCoords.x()*oRad, lineCoords.y()*oRad)); } } painter.restore(); } void KisColorSelector::drawLightStrip(QPainter& painter, const QRect& rect) { qreal penSize = qreal(qMin(QWidget::width(), QWidget::height())) / 200.0; qreal penSizeSmall = penSize / 1.2; QPen selectedPen; KisColor valueScaleColor(m_selectedColor, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); KisColor grayScaleColor(Qt::gray, m_colorConverter, m_colorSpace, m_lumaR, m_lumaG, m_lumaB, m_lumaGamma); int rectSize = rect.height(); painter.save(); painter.resetTransform(); painter.setRenderHint(QPainter::Antialiasing, true); QTransform matrix; matrix.translate(rect.x(), rect.y()); matrix.scale(rect.width(), rect.height()); qreal rectColorLeftX; qreal rectColorWidth; if (m_showValueScaleNumbers) { rectColorLeftX = 0.6; rectColorWidth = 0.4; } else { rectColorLeftX = 0.0; rectColorWidth = 1.0; } if (getNumLightPieces() > 1) { for(int i=0; i 0) && (fm.boundingRect("100%").width() > rect.width()*rectColorLeftX)) { font.setPointSize(font.pointSize() - 1); painter.setFont(font); fm = painter.fontMetrics(); retries--; } for(int i=0; i 0) { int valueNumber = 0; if (m_colorSpace == KisColor::HSY) { valueNumber = 100 - round(pow(pow(grayScaleColor.getX(), m_lumaGamma), 1.0/2.2)*100); } else { valueNumber = 100 - grayScaleColor.getX()*100; } if (valueNumber < 55) { painter.setPen(QPen(QBrush(COLOR_DARK), penSize)); } else { painter.setPen(QPen(QBrush(COLOR_LIGHT), penSize)); } painter.drawText(rectValue, Qt::AlignRight|Qt::AlignBottom, QString("%1%").arg(QString::number(valueNumber))); } } } painter.restore(); } void KisColorSelector::drawBlip(QPainter& painter, const QRect& rect) { painter.save(); painter.setRenderHint(QPainter::Antialiasing, true); painter.resetTransform(); painter.translate(rect.x() + rect.width()/2, rect.y() + rect.height()/2); painter.scale(rect.width()/2, rect.height()/2); QPointF fgColorPos = mapColorToUnit(m_fgColor); #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::drawBlip: " << "colorPoint H:" << m_fgColor.getH() << " S:" << m_fgColor.getS() << "-> coord X:" << fgColorPos.x() << " Y:" << fgColorPos.y(); #endif painter.setPen(QPen(QBrush(COLOR_SELECTED_DARK), 0.01)); painter.drawEllipse(fgColorPos, 0.05, 0.05); painter.setPen(QPen(QBrush(COLOR_SELECTED_LIGHT), 0.01)); painter.drawEllipse(fgColorPos, 0.04, 0.04); painter.restore(); } void KisColorSelector::drawGamutMaskShape(QPainter &painter, const QRect &rect) { painter.save(); painter.setRenderHint(QPainter::Antialiasing, true); painter.resetTransform(); painter.translate(rect.width()/2, rect.height()/2); painter.scale(rect.width()/2, rect.height()/2); painter.setPen(Qt::NoPen); painter.setBrush(COLOR_MASK_FILL); painter.drawEllipse(QPointF(0,0), 1.0, 1.0); painter.resetTransform(); painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); m_currentGamutMask->paint(painter, *m_viewConverter, m_maskPreviewActive); painter.setCompositionMode(QPainter::CompositionMode_SourceOver); m_currentGamutMask->paintStroke(painter, *m_viewConverter, m_maskPreviewActive); painter.restore(); } void KisColorSelector::drawColorPreview(QPainter &painter, const QRect &rect) { painter.save(); painter.setRenderHint(QPainter::Antialiasing, true); painter.fillRect(rect, m_fgColor.toQColor()); int bgSide = qMin(rect.width()*0.15,rect.height()*0.15); if (m_showBgColor) { QPointF bgPolyPoints[3] = { QPointF(rect.width(), rect.height()), QPointF(rect.width()-bgSide, rect.height()), QPointF(rect.width(), rect.height()-bgSide) }; painter.setBrush(m_bgColor.toQColor()); painter.setPen(m_bgColor.toQColor()); painter.drawPolygon(bgPolyPoints, 3); } painter.restore(); } void KisColorSelector::paintEvent(QPaintEvent* /*event*/) { QPainter wdgPainter(this); // draw the fg and bg color previews if (m_isDirtyColorPreview) { m_colorPreviewBuffer.fill(Qt::transparent); QPainter colorPreviewPainter(&m_colorPreviewBuffer); drawColorPreview(colorPreviewPainter, m_widgetArea); m_isDirtyColorPreview = false; } wdgPainter.drawImage(m_widgetArea, m_colorPreviewBuffer); // draw the fg and bg color previews // draw the wheel if (m_isDirtyWheel) { m_renderBuffer.fill(Qt::transparent); QPainter wheelPainter(&m_renderBuffer); for(int i=0; ilocalPos(), m_renderArea); m_mouseMoved = false; m_pressedButtons = event->buttons(); m_clickedRing = getSaturationIndex(m_clickPos); Acs::ColorRole colorRole = Acs::buttonsToRole(Qt::NoButton, m_pressedButtons); qint8 clickedLightPiece = getLightIndex(event->localPos()); if (clickedLightPiece >= 0) { setLight(getLight(event->localPos())); m_selectedLightPiece = clickedLightPiece; requestUpdateColorAndPreview(m_selectedColor, colorRole); m_mouseMoved = true; } else if (m_clickedRing >= 0) { if (getNumPieces() == 1) { Radian angle = mapCoordToAngle(m_clickPos.x(), m_clickPos.y()); KisColor color(m_colorConverter, m_colorSpace); color.setHSX(angle.scaled(0.0, 1.0) , getSaturation(m_clickedRing) , m_selectedColor.getX() ); #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::mousePressEvent: picked color: " << "H:" << color.getH() << "S:" << color.getS() << "X:" << color.getX(); #endif if ((!m_enforceGamutMask) || colorIsClear(color)) { m_selectedColor.setHSX(color.getH(), color.getS(), color.getX()); requestUpdateColorAndPreview(m_selectedColor, colorRole); m_selectedRing = m_clickedRing; m_mouseMoved = true; update(); } } } } void KisColorSelector::mouseMoveEvent(QMouseEvent* event) { QPointF dragPos = mapCoordToUnit(event->localPos(), m_renderArea); qint8 clickedLightPiece = getLightIndex(event->localPos()); Acs::ColorRole colorRole = Acs::buttonsToRole(Qt::NoButton, m_pressedButtons); if (clickedLightPiece >= 0) { setLight(getLight(event->localPos())); m_selectedLightPiece = clickedLightPiece; requestUpdateColorAndPreview(m_selectedColor, colorRole); } if (m_clickedRing < 0) return; if (getNumPieces() == 1) { Radian angle = mapCoordToAngle(dragPos.x(), dragPos.y()); KisColor color(m_colorConverter, m_colorSpace); color.setHSX(angle.scaled(0.0, 1.0) , getSaturation(m_clickedRing) , m_selectedColor.getX() ); if ((!m_enforceGamutMask) || colorIsClear(color)) { m_selectedColor.setHSX(color.getH(), color.getS(), color.getX()); requestUpdateColorAndPreview(m_selectedColor, colorRole); } } update(); } void KisColorSelector::mouseReleaseEvent(QMouseEvent* /*event*/) { Acs::ColorRole colorRole = Acs::buttonsToRole(Qt::NoButton, m_pressedButtons); if (!m_mouseMoved && m_clickedRing >= 0) { Radian angle = mapCoordToAngle(m_clickPos.x(), m_clickPos.y()); KisColor color(m_colorConverter, m_colorSpace); qint8 hueIndex = getHueIndex(angle); if (getNumPieces() > 1) { color.setH(getHue(hueIndex)); } else { color.setH(angle.scaled(0.0, 1.0)); } color.setS(getSaturation(m_clickedRing)); color.setX(m_selectedColor.getX()); if ((!m_enforceGamutMask) || colorIsClear(color)) { m_selectedColor.setHSX(color.getH(), color.getS(), color.getX()); m_selectedPiece = hueIndex; m_selectedRing = m_clickedRing; requestUpdateColorAndPreview(m_selectedColor, colorRole); } } else if (m_mouseMoved) requestUpdateColorAndPreview(m_selectedColor, colorRole); m_clickedRing = -1; m_widgetUpdatesSelf = false; #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::ReleasePressEvent: m_widgetUpdatesSelf = false"; #endif update(); } void KisColorSelector::resizeEvent(QResizeEvent* /*event*/) { recalculateAreas(quint8(getNumLightPieces())); } void KisColorSelector::leaveEvent(QEvent* /*e*/) { m_widgetUpdatesSelf = false; #ifdef DEBUG_ARC_SELECTOR dbgPlugins << "KisColorSelector::leaveEvent: m_widgetUpdatesSelf = false"; #endif } void KisColorSelector::saveSettings() { KisConfig cfg(false); cfg.writeEntry("ArtColorSel.ColorSpace" , qint32(m_colorSpace)); cfg.writeEntry("ArtColorSel.lumaR", qreal(m_lumaR)); cfg.writeEntry("ArtColorSel.lumaG", qreal(m_lumaG)); cfg.writeEntry("ArtColorSel.lumaB", qreal(m_lumaB)); cfg.writeEntry("ArtColorSel.lumaGamma", qreal(m_lumaGamma)); cfg.writeEntry("ArtColorSel.NumRings" , m_colorRings.size()); cfg.writeEntry("ArtColorSel.RingPieces" , qint32(m_numPieces)); cfg.writeEntry("ArtColorSel.LightPieces", qint32(m_numLightPieces)); cfg.writeEntry("ArtColorSel.InversedSaturation", m_inverseSaturation); cfg.writeEntry("ArtColorSel.Light" , m_selectedColor.getX()); cfg.writeEntry("ArtColorSel.SelColorH", m_selectedColor.getH()); cfg.writeEntry("ArtColorSel.SelColorS", m_selectedColor.getS()); cfg.writeEntry("ArtColorSel.SelColorX", m_selectedColor.getX()); cfg.writeEntry("ArtColorSel.defaultHueSteps", quint32(m_defaultHueSteps)); cfg.writeEntry("ArtColorSel.defaultSaturationSteps", quint32(m_defaultSaturationSteps)); cfg.writeEntry("ArtColorSel.defaultValueScaleSteps", quint32(m_defaultValueScaleSteps)); cfg.writeEntry("ArtColorSel.showBgColor", m_showBgColor); cfg.writeEntry("ArtColorSel.showValueScale", m_showValueScaleNumbers); cfg.writeEntry("ArtColorSel.enforceGamutMask", m_enforceGamutMask); } void KisColorSelector::loadSettings() { KisConfig cfg(true); m_defaultHueSteps = cfg.readEntry("ArtColorSel.defaultHueSteps", DEFAULT_HUE_STEPS); m_defaultSaturationSteps = cfg.readEntry("ArtColorSel.defaultSaturationSteps", DEFAULT_SATURATION_STEPS); m_defaultValueScaleSteps = cfg.readEntry("ArtColorSel.defaultValueScaleSteps", DEFAULT_VALUE_SCALE_STEPS); setNumLightPieces(cfg.readEntry("ArtColorSel.LightPieces", DEFAULT_VALUE_SCALE_STEPS)); KisColor::Type colorSpace = KisColor::Type(cfg.readEntry("ArtColorSel.ColorSpace" , KisColor::HSY)); setColorSpace(colorSpace); setLumaCoefficients(cfg.readEntry("ArtColorSel.lumaR", DEFAULT_LUMA_R), cfg.readEntry("ArtColorSel.lumaG", DEFAULT_LUMA_G), cfg.readEntry("ArtColorSel.lumaB", DEFAULT_LUMA_B), cfg.readEntry("ArtColorSel.lumaGamma", DEFAULT_LUMA_GAMMA)); m_selectedColor.setH(cfg.readEntry("ArtColorSel.SelColorH", 0.0)); m_selectedColor.setS(cfg.readEntry("ArtColorSel.SelColorS", 0.0)); m_selectedColor.setX(cfg.readEntry("ArtColorSel.SelColorX", 0.0)); setInverseSaturation(cfg.readEntry("ArtColorSel.InversedSaturation", false)); setLight(cfg.readEntry("ArtColorSel.Light", 0.5f)); setNumRings(cfg.readEntry("ArtColorSel.NumRings", DEFAULT_SATURATION_STEPS)); setNumPieces(cfg.readEntry("ArtColorSel.RingPieces", DEFAULT_HUE_STEPS)); m_showBgColor = cfg.readEntry("ArtColorSel.showBgColor", true); m_showValueScaleNumbers = cfg.readEntry("ArtColorSel.showValueScale", false); m_enforceGamutMask = cfg.readEntry("ArtColorSel.enforceGamutMask", false); selectColor(m_selectedColor); update(); } void KisColorSelector::setDefaultHueSteps(int num) { num = qBound(MIN_NUM_HUE_PIECES, num, MAX_NUM_HUE_PIECES); m_defaultHueSteps = num; } void KisColorSelector::setDefaultSaturationSteps(int num) { num = qBound(MIN_NUM_SATURATION_RINGS, num, MAX_NUM_SATURATION_RINGS); m_defaultSaturationSteps = num; } void KisColorSelector::setDefaultValueScaleSteps(int num) { num = qBound(MIN_NUM_LIGHT_PIECES, num, MAX_NUM_LIGHT_PIECES); m_defaultValueScaleSteps = num; } void KisColorSelector::setShowBgColor(bool value) { m_showBgColor = value; m_isDirtyColorPreview = true; update(); } void KisColorSelector::setShowValueScaleNumbers(bool value) { m_showValueScaleNumbers = value; recalculateAreas(quint8(getNumLightPieces())); update(); } diff --git a/plugins/dockers/artisticcolorselector/kis_color_selector.h b/plugins/dockers/artisticcolorselector/kis_color_selector.h index 30666fde44..362b105756 100644 --- a/plugins/dockers/artisticcolorselector/kis_color_selector.h +++ b/plugins/dockers/artisticcolorselector/kis_color_selector.h @@ -1,209 +1,209 @@ /* Copyright (C) 2011 Silvio Heinrich 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 H_KIS_COLOR_SELECTOR_H #define H_KIS_COLOR_SELECTOR_H #include #include #include #include #include "kis_color.h" #include "kis_radian.h" #include "kis_acs_types.h" #include "kis_signal_compressor_with_param.h" #include #include class QPainter; class KisDisplayColorConverter; class KisColorSelector: public QWidget { Q_OBJECT typedef KisRadian Radian; struct ColorRing { ColorRing() : saturation(0) , outerRadius(0) , innerRadius(0) { } qreal saturation; qreal outerRadius; qreal innerRadius; QVector pieced; }; public: KisColorSelector(QWidget* parent, KisColor::Type type=KisColor::HSL); void setColorSpace(KisColor::Type type); void setColorConverter(KisDisplayColorConverter* colorConverter); void setNumPieces(int num); void setNumLightPieces(int num) __attribute__((optimize(0))); void setNumRings(int num); void setLight(qreal light=0.0f); void setLumaCoefficients(qreal lR, qreal lG, qreal lB, qreal lGamma); inline qreal lumaR() const { return m_lumaR; } inline qreal lumaG() const { return m_lumaG; } inline qreal lumaB() const { return m_lumaB; } inline qreal lumaGamma() const { return m_lumaGamma; } void setInverseSaturation(bool inverse); void selectColor(const KisColor& color); void setFgColor(const KoColor& fgColor); void setBgColor(const KoColor& bgColor); void setDefaultHueSteps(int num); void setDefaultSaturationSteps(int num); void setDefaultValueScaleSteps(int num); void setShowBgColor(bool value); void setShowValueScaleNumbers(bool value); void setGamutMask(KoGamutMask* gamutMask); void setDirty(); bool gamutMaskOn(); void setGamutMaskOn(bool gamutMaskOn); void setEnforceGamutMask(bool enforce); KoGamutMask* gamutMask(); void saveSettings(); void loadSettings(); KisColor::Type getColorSpace () const { return m_colorSpace; } qint32 getNumRings () const { return m_colorRings.size(); } qint32 getNumPieces () const { return m_numPieces; } qint32 getNumLightPieces () const { return m_numLightPieces; } bool isSaturationInverted() const { return m_inverseSaturation; } quint32 getDefaultHueSteps () const { return m_defaultHueSteps; } quint32 getDefaultSaturationSteps () const { return m_defaultSaturationSteps; } quint32 getDefaultValueScaleSteps () const { return m_defaultValueScaleSteps; } bool getShowBgColor () const { return m_showBgColor; } bool getShowValueScaleNumbers () const { return m_showValueScaleNumbers; } bool enforceGamutMask () const { return m_enforceGamutMask; } Q_SIGNALS: void sigFgColorChanged(const KisColor& color); void sigBgColorChanged(const KisColor& color); private: void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void resizeEvent(QResizeEvent* event) override; void paintEvent(QPaintEvent* event) override; void leaveEvent(QEvent* e) override; bool colorIsClear(const KisColor &color); bool colorIsClear(const QPointF &colorPoint); void requestUpdateColorAndPreview(const KisColor &color, Acs::ColorRole role); void recalculateAreas(quint8 numLightPieces); void recalculateRings(quint8 numRings, quint8 numPieces); void createRing(ColorRing& wheel, quint8 numPieces, qreal innerRadius, qreal outerRadius); void drawRing(QPainter& painter, ColorRing& wheel, const QRect& rect); void drawOutline(QPainter& painter, const QRect& rect); void drawBlip(QPainter& painter, const QRect& rect); void drawLightStrip(QPainter& painter, const QRect& rect); void drawGamutMaskShape(QPainter& painter, const QRect& rect); void drawColorPreview(QPainter& painter, const QRect& rect); qint8 getHueIndex(Radian hue) const; qreal getHue(int hueIdx, Radian shift=0.0f) const; qint8 getLightIndex(const QPointF& pt) const; qint8 getLightIndex(qreal light) const; qreal getLight(const QPointF& pt) const; qint8 getSaturationIndex(const QPointF& pt) const; qint8 getSaturationIndex(qreal saturation) const; qreal getSaturation(int saturationIdx) const; QPointF mapCoordToView(const QPointF& pt, const QRectF& viewRect) const; QPointF mapCoordToUnit(const QPointF& pt, const QRectF& viewRect) const; QPointF mapColorToUnit(const KisColor& color, bool invertSaturation = true) const; Radian mapCoordToAngle(qreal x, qreal y) const; QPointF mapHueToAngle(qreal hue) const; public: // This is a private interface for signal compressor, don't use it. // Use requestUpdateColorAndPreview() instead void slotUpdateColorAndPreview(QPair color); private: KisDisplayColorConverter* m_colorConverter; KisColor::Type m_colorSpace; quint8 m_numPieces; quint8 m_numLightPieces; bool m_inverseSaturation; qint8 m_selectedRing; qint8 m_selectedPiece; qint8 m_selectedLightPiece; KisColor m_selectedColor; KisColor m_fgColor; KisColor m_bgColor; QImage m_renderBuffer; QImage m_maskBuffer; QImage m_lightStripBuffer; QImage m_colorPreviewBuffer; QRect m_widgetArea; QRect m_renderArea; QRect m_lightStripArea; bool m_mouseMoved; QPointF m_clickPos; qint8 m_clickedRing; QVector m_colorRings; Qt::MouseButtons m_pressedButtons; // docker settings quint8 m_defaultHueSteps; quint8 m_defaultSaturationSteps; quint8 m_defaultValueScaleSteps; - bool m_showValueScaleNumbers; - bool m_showBgColor; + bool m_showValueScaleNumbers {false}; + bool m_showBgColor {true}; bool m_gamutMaskOn; KoGamutMask* m_currentGamutMask; bool m_enforceGamutMask; QSize m_renderAreaSize; bool m_maskPreviewActive; KisGamutMaskViewConverter* m_viewConverter; bool m_widgetUpdatesSelf; bool m_isDirtyWheel; bool m_isDirtyLightStrip; bool m_isDirtyGamutMask; bool m_isDirtyColorPreview; qreal m_lumaR; qreal m_lumaG; qreal m_lumaB; qreal m_lumaGamma; typedef KisSignalCompressorWithParam> ColorCompressorType; QScopedPointer m_updateColorCompressor; }; #endif // H_KIS_COLOR_SELECTOR_H diff --git a/plugins/dockers/griddocker/grid_config_widget.cpp b/plugins/dockers/griddocker/grid_config_widget.cpp index 56d1d6e08f..b6f8d934d2 100644 --- a/plugins/dockers/griddocker/grid_config_widget.cpp +++ b/plugins/dockers/griddocker/grid_config_widget.cpp @@ -1,365 +1,362 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 "grid_config_widget.h" #include "ui_grid_config_widget.h" #include "kis_grid_config.h" #include "kis_guides_config.h" #include "kis_debug.h" #include "kis_aspect_ratio_locker.h" #include "kis_int_parse_spin_box.h" #include #include #include #include struct GridConfigWidget::Private { Private() : guiSignalsBlocked(false) {} KisGridConfig gridConfig; KisGuidesConfig guidesConfig; - bool guiSignalsBlocked; + bool guiSignalsBlocked {false}; }; GridConfigWidget::GridConfigWidget(QWidget *parent) : QWidget(parent), ui(new Ui::GridConfigWidget), m_d(new Private) { ui->setupUi(this); ui->colorMain->setAlphaChannelEnabled(true); ui->colorSubdivision->setAlphaChannelEnabled(true); ui->colorGuides->setAlphaChannelEnabled(true); ui->angleLeftSpinbox->setSuffix(QChar(Qt::Key_degree)); ui->angleRightSpinbox->setSuffix(QChar(Qt::Key_degree)); ui->cellSpacingSpinbox->setSuffix(i18n(" px")); ui->gridTypeCombobox->addItem(i18n("Rectangle")); ui->gridTypeCombobox->addItem(i18n("Isometric")); ui->gridTypeCombobox->setCurrentIndex(0); // set to rectangle by default slotGridTypeChanged(); // update the UI to hide any elements we don't need connect(ui->gridTypeCombobox, SIGNAL(currentIndexChanged(int)), SLOT(slotGridTypeChanged())); - - m_isGridEnabled = false; - setGridConfig(m_d->gridConfig); setGuidesConfig(m_d->guidesConfig); // hide offset UI elements if offset is disabled connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->lblXOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->lblYOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->intXOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->intYOffset, SLOT(setVisible(bool))); connect(ui->chkOffset, SIGNAL(toggled(bool)), ui->offsetAspectButton, SLOT(setVisible(bool))); ui->lblXOffset->setVisible(false); ui->lblYOffset->setVisible(false); ui->intXOffset->setVisible(false); ui->intYOffset->setVisible(false); ui->offsetAspectButton->setVisible(false); connect(ui->chkShowGrid, SIGNAL(stateChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->chkSnapToGrid, SIGNAL(stateChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->chkShowGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->chkSnapToGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->chkLockGuides, SIGNAL(stateChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->intSubdivision, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->angleLeftSpinbox, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->angleRightSpinbox, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->cellSpacingSpinbox, SIGNAL(valueChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->selectMainStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->colorMain, SIGNAL(changed(QColor)), SLOT(slotGridGuiChanged())); connect(ui->selectSubdivisionStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGridGuiChanged())); connect(ui->colorSubdivision, SIGNAL(changed(QColor)), SLOT(slotGridGuiChanged())); connect(ui->selectGuidesStyle, SIGNAL(currentIndexChanged(int)), SLOT(slotGuidesGuiChanged())); connect(ui->colorGuides, SIGNAL(changed(QColor)), SLOT(slotGuidesGuiChanged())); ui->chkOffset->setChecked(false); KisAspectRatioLocker *offsetLocker = new KisAspectRatioLocker(this); offsetLocker->connectSpinBoxes(ui->intXOffset, ui->intYOffset, ui->offsetAspectButton); KisAspectRatioLocker *spacingLocker = new KisAspectRatioLocker(this); spacingLocker->connectSpinBoxes(ui->intHSpacing, ui->intVSpacing, ui->spacingAspectButton); connect(offsetLocker, SIGNAL(sliderValueChanged()), SLOT(slotGridGuiChanged())); connect(offsetLocker, SIGNAL(aspectButtonChanged()), SLOT(slotGridGuiChanged())); connect(spacingLocker, SIGNAL(sliderValueChanged()), SLOT(slotGridGuiChanged())); connect(spacingLocker, SIGNAL(aspectButtonChanged()), SLOT(slotGridGuiChanged())); connect(ui->chkShowRulers,SIGNAL(toggled(bool)),SIGNAL(showRulersChanged(bool))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotPreferencesUpdated())); } GridConfigWidget::~GridConfigWidget() { delete ui; } void GridConfigWidget::setGridConfig(const KisGridConfig &value) { KisGridConfig currentConfig = fetchGuiGridConfig(); if (currentConfig == value) return; setGridConfigImpl(value); } void GridConfigWidget::setGuidesConfig(const KisGuidesConfig &value) { KisGuidesConfig currentConfig = fetchGuiGuidesConfig(); if (currentConfig == value) return; setGuidesConfigImpl(value); } void GridConfigWidget::setGridConfigImpl(const KisGridConfig &value) { m_d->gridConfig = value; m_d->guiSignalsBlocked = true; ui->offsetAspectButton->setKeepAspectRatio(m_d->gridConfig.offsetAspectLocked()); ui->spacingAspectButton->setKeepAspectRatio(m_d->gridConfig.spacingAspectLocked()); ui->chkShowGrid->setChecked(m_d->gridConfig.showGrid()); ui->intHSpacing->setValue(m_d->gridConfig.spacing().x()); ui->intVSpacing->setValue(m_d->gridConfig.spacing().y()); ui->intXOffset->setValue(m_d->gridConfig.offset().x()); ui->intYOffset->setValue(m_d->gridConfig.offset().y()); ui->intSubdivision->setValue(m_d->gridConfig.subdivision()); ui->chkSnapToGrid->setChecked(m_d->gridConfig.snapToGrid()); ui->angleLeftSpinbox->setValue(m_d->gridConfig.angleLeft()); ui->angleRightSpinbox->setValue(m_d->gridConfig.angleRight()); ui->cellSpacingSpinbox->setValue(m_d->gridConfig.cellSpacing()); ui->selectMainStyle->setCurrentIndex(int(m_d->gridConfig.lineTypeMain())); ui->selectSubdivisionStyle->setCurrentIndex(int(m_d->gridConfig.lineTypeSubdivision())); ui->gridTypeCombobox->setCurrentIndex(m_d->gridConfig.gridType()); ui->colorMain->setColor(m_d->gridConfig.colorMain()); ui->colorSubdivision->setColor(m_d->gridConfig.colorSubdivision()); m_d->guiSignalsBlocked = false; emit gridValueChanged(); } void GridConfigWidget::setGuidesConfigImpl(const KisGuidesConfig &value) { m_d->guidesConfig = value; m_d->guiSignalsBlocked = true; ui->chkShowGuides->setChecked(m_d->guidesConfig.showGuides()); ui->chkSnapToGuides->setChecked(m_d->guidesConfig.snapToGuides()); ui->chkLockGuides->setChecked(m_d->guidesConfig.lockGuides()); ui->selectGuidesStyle->setCurrentIndex(int(m_d->guidesConfig.guidesLineType())); ui->colorGuides->setColor(m_d->guidesConfig.guidesColor()); m_d->guiSignalsBlocked = false; emit guidesValueChanged(); } KisGridConfig GridConfigWidget::gridConfig() const { return m_d->gridConfig; } KisGuidesConfig GridConfigWidget::guidesConfig() const { return m_d->guidesConfig; } void GridConfigWidget::setGridDivision(int w, int h) { ui->intHSpacing->setMaximum(w); ui->intVSpacing->setMaximum(h); } KisGridConfig GridConfigWidget::fetchGuiGridConfig() const { KisGridConfig config; config.setShowGrid(ui->chkShowGrid->isChecked()); config.setSnapToGrid(ui->chkSnapToGrid->isChecked()); QPoint pt; pt.rx() = ui->intHSpacing->value(); pt.ry() = ui->intVSpacing->value(); config.setSpacing(pt); pt.rx() = ui->intXOffset->value(); pt.ry() = ui->intYOffset->value(); config.setOffset(pt); config.setSubdivision(ui->intSubdivision->value()); config.setAngleLeft(ui->angleLeftSpinbox->value()); config.setAngleRight(ui->angleRightSpinbox->value()); config.setCellSpacing(ui->cellSpacingSpinbox->value()); config.setGridType(ui->gridTypeCombobox->currentIndex()); config.setOffsetAspectLocked(ui->offsetAspectButton->keepAspectRatio()); config.setSpacingAspectLocked(ui->spacingAspectButton->keepAspectRatio()); config.setLineTypeMain(KisGridConfig::LineTypeInternal(ui->selectMainStyle->currentIndex())); config.setLineTypeSubdivision(KisGridConfig::LineTypeInternal(ui->selectSubdivisionStyle->currentIndex())); config.setColorMain(ui->colorMain->color()); config.setColorSubdivision(ui->colorSubdivision->color()); return config; } KisGuidesConfig GridConfigWidget::fetchGuiGuidesConfig() const { KisGuidesConfig config = m_d->guidesConfig; config.setShowGuides(ui->chkShowGuides->isChecked()); config.setSnapToGuides(ui->chkSnapToGuides->isChecked()); config.setLockGuides(ui->chkLockGuides->isChecked()); config.setGuidesLineType(KisGuidesConfig::LineTypeInternal(ui->selectGuidesStyle->currentIndex())); config.setGuidesColor(ui->colorGuides->color()); return config; } void GridConfigWidget::slotGridGuiChanged() { if (m_d->guiSignalsBlocked) return; KisGridConfig currentConfig = fetchGuiGridConfig(); if (currentConfig == m_d->gridConfig) return; setGridConfigImpl(currentConfig); } void GridConfigWidget::slotPreferencesUpdated() { KisConfig cfg(true); enableIsometricGrid(cfg.useOpenGL()); // Isometric view needs OpenGL } void GridConfigWidget::slotGuidesGuiChanged() { if (m_d->guiSignalsBlocked) return; KisGuidesConfig currentConfig = fetchGuiGuidesConfig(); if (currentConfig == m_d->guidesConfig) return; setGuidesConfigImpl(currentConfig); } void GridConfigWidget::slotGridTypeChanged() { bool showRectangleControls = ui->gridTypeCombobox->currentIndex() == 0; // specific rectangle UI controls ui->lblXSpacing->setVisible(showRectangleControls); ui->lblYSpacing->setVisible(showRectangleControls); ui->intHSpacing->setVisible(showRectangleControls); ui->intVSpacing->setVisible(showRectangleControls); ui->spacingAspectButton->setVisible(showRectangleControls); ui->lblSubdivision->setVisible(showRectangleControls); ui->intSubdivision->setVisible(showRectangleControls); ui->lblSubdivisionStyle->setVisible(showRectangleControls); ui->selectSubdivisionStyle->setVisible(showRectangleControls); ui->colorSubdivision->setVisible(showRectangleControls); // specific isometric UI controls ui->leftAngleLabel->setVisible(!showRectangleControls); ui->rightAngleLabel->setVisible(!showRectangleControls); ui->angleLeftSpinbox->setVisible(!showRectangleControls); ui->angleRightSpinbox->setVisible(!showRectangleControls); ui->cellSpacingLabel->setVisible(!showRectangleControls); ui->cellSpacingSpinbox->setVisible(!showRectangleControls); // disable snapping for isometric grid type for now // remember if we had snapping enabled if it was on the rectangule mode if (!showRectangleControls) { m_isGridEnabled = ui->chkSnapToGrid->isChecked(); ui->chkSnapToGrid->setEnabled(false); ui->chkSnapToGrid->setChecked(false); } else { ui->chkSnapToGrid->setEnabled(true); ui->chkSnapToGrid->setChecked(m_isGridEnabled); } slotGridGuiChanged(); } bool GridConfigWidget::showRulers() const { return ui->chkShowRulers->isChecked(); } void GridConfigWidget::enableIsometricGrid(bool value) { m_isIsometricGridEnabled = value; // Isometric grids disabled if OpenGL is disabled QStandardItemModel *model = qobject_cast(ui->gridTypeCombobox->model()); QStandardItem *item = model->item(1); // isometric option // item->setFlags(m_isIsometricGridEnabled ? item->flags() & ~Qt::ItemIsEnabled: // item->flags() | Qt::ItemIsEnabled); item->setEnabled(m_isIsometricGridEnabled); if (m_isIsometricGridEnabled) { item->setText(i18n("Isometric")); } else { item->setText(i18n("Isometric (requires OpenGL)")); // change drop down index to Rectangular in case it was previously set to isometric ui->gridTypeCombobox->setCurrentIndex(0); } } void GridConfigWidget::setShowRulers(bool value) { ui->chkShowRulers->setChecked(value); } diff --git a/plugins/dockers/griddocker/grid_config_widget.h b/plugins/dockers/griddocker/grid_config_widget.h index 785888a369..d308974195 100644 --- a/plugins/dockers/griddocker/grid_config_widget.h +++ b/plugins/dockers/griddocker/grid_config_widget.h @@ -1,83 +1,83 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser 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 GRID_CONFIG_WIDGET_H #define GRID_CONFIG_WIDGET_H #include #include namespace Ui { class GridConfigWidget; } class KisGridConfig; class KisGuidesConfig; class GridConfigWidget : public QWidget { Q_OBJECT public: explicit GridConfigWidget(QWidget *parent = 0); ~GridConfigWidget() override; void setGridConfig(const KisGridConfig &value); KisGridConfig gridConfig() const; void setGuidesConfig(const KisGuidesConfig &value); KisGuidesConfig guidesConfig() const; void setGridDivision(int w, int h); bool showRulers() const; void enableIsometricGrid(bool value); public Q_SLOTS: void setShowRulers(bool value); private Q_SLOTS: void slotGridGuiChanged(); void slotGuidesGuiChanged(); void slotGridTypeChanged(); void slotPreferencesUpdated(); Q_SIGNALS: void gridValueChanged(); void guidesValueChanged(); void showRulersChanged(bool); private: KisGridConfig fetchGuiGridConfig() const; void setGridConfigImpl(const KisGridConfig &value); KisGuidesConfig fetchGuiGuidesConfig() const; void setGuidesConfigImpl(const KisGuidesConfig &value); private: Ui::GridConfigWidget *ui; struct Private; const QScopedPointer m_d; - bool m_isGridEnabled; + bool m_isGridEnabled {false}; - bool m_isIsometricGridEnabled = true; + bool m_isIsometricGridEnabled {true}; }; #endif // GRID_CONFIG_WIDGET_H