diff --git a/plugins/tools/defaulttool/connectionTool/ChangeConnectionPointCommand.cpp b/plugins/tools/defaulttool/connectionTool/ChangeConnectionPointCommand.cpp index d7cb3dcafa..f26e31b0f5 100644 --- a/plugins/tools/defaulttool/connectionTool/ChangeConnectionPointCommand.cpp +++ b/plugins/tools/defaulttool/connectionTool/ChangeConnectionPointCommand.cpp @@ -1,62 +1,62 @@ /* This file is part of the KDE project * * Copyright (C) 2011 Jan Hambrecht * * 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 "ChangeConnectionPointCommand.h" #include ChangeConnectionPointCommand::ChangeConnectionPointCommand(KoShape *shape, int connectionPointId, const KoConnectionPoint &oldPoint, const KoConnectionPoint &newPoint, KUndo2Command *parent) : KUndo2Command(parent) , m_shape(shape) , m_connectionPointId(connectionPointId) , m_oldPoint(oldPoint) , m_newPoint(newPoint) { Q_ASSERT(m_shape); } ChangeConnectionPointCommand::~ChangeConnectionPointCommand() { } void ChangeConnectionPointCommand::redo() { updateRoi(m_oldPoint.position); m_shape->setConnectionPoint(m_connectionPointId, m_newPoint); updateRoi(m_newPoint.position); KUndo2Command::redo(); } void ChangeConnectionPointCommand::undo() { KUndo2Command::undo(); updateRoi(m_newPoint.position); m_shape->setConnectionPoint(m_connectionPointId, m_oldPoint); updateRoi(m_oldPoint.position); } -void ChangeConnectionPointCommand::updateRoi(const QPointF &position) +void ChangeConnectionPointCommand::updateRoi(const QPointF &/*position*/) { // TODO: is there a way we can get at the correct update size? //QRectF roi(0, 0, 10, 10); //roi.moveCenter(position); //m_shape->update(roi); } diff --git a/plugins/tools/defaulttool/defaulttool/DefaultToolGeometryWidget.cpp b/plugins/tools/defaulttool/defaulttool/DefaultToolGeometryWidget.cpp index ca64f24159..18053bf125 100644 --- a/plugins/tools/defaulttool/defaulttool/DefaultToolGeometryWidget.cpp +++ b/plugins/tools/defaulttool/defaulttool/DefaultToolGeometryWidget.cpp @@ -1,463 +1,463 @@ /* This file is part of the KDE project * Copyright (C) 2007 Martin Pfeiffer * Copyright (C) 2007 Jan Hambrecht Copyright (C) 2008 Thorsten Zachmann * Copyright (C) 2010 Thomas Zander * * 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 "DefaultToolGeometryWidget.h" #include "DefaultTool.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "SelectionDecorator.h" #include #include "KoAnchorSelectionWidget.h" #include #include #include #include #include #include #include #include #include #include "kis_aspect_ratio_locker.h" #include "kis_debug.h" #include "kis_acyclic_signal_connector.h" #include "kis_signal_compressor.h" #include "kis_signals_blocker.h" DefaultToolGeometryWidget::DefaultToolGeometryWidget(KoInteractionTool *tool, QWidget *parent) : QWidget(parent) , m_tool(tool) , m_sizeAspectLocker(new KisAspectRatioLocker()) , m_savedUniformScaling(false) { setupUi(this); setUnit(m_tool->canvas()->unit()); // Connect and initialize automated aspect locker m_sizeAspectLocker->connectSpinBoxes(widthSpinBox, heightSpinBox, aspectButton); aspectButton->setKeepAspectRatio(false); // TODO: use valueChanged() instead! connect(positionXSpinBox, SIGNAL(valueChangedPt(qreal)), this, SLOT(slotRepositionShapes())); connect(positionYSpinBox, SIGNAL(valueChangedPt(qreal)), this, SLOT(slotRepositionShapes())); KoSelectedShapesProxy *selectedShapesProxy = m_tool->canvas()->selectedShapesProxy(); connect(selectedShapesProxy, SIGNAL(selectionChanged()), this, SLOT(slotUpdateCheckboxes())); connect(selectedShapesProxy, SIGNAL(selectionChanged()), this, SLOT(slotUpdatePositionBoxes())); connect(selectedShapesProxy, SIGNAL(selectionChanged()), this, SLOT(slotUpdateOpacitySlider())); connect(selectedShapesProxy, SIGNAL(selectionContentChanged()), this, SLOT(slotUpdatePositionBoxes())); connect(selectedShapesProxy, SIGNAL(selectionContentChanged()), this, SLOT(slotUpdateOpacitySlider())); connect(chkGlobalCoordinates, SIGNAL(toggled(bool)), SLOT(slotUpdateSizeBoxes())); /** * A huge block of self-blocking acycled connections */ KisAcyclicSignalConnector *acyclicConnector = new KisAcyclicSignalConnector(this); acyclicConnector->connectForwardVoid(m_sizeAspectLocker.data(), SIGNAL(aspectButtonChanged()), this, SLOT(slotAspectButtonToggled())); acyclicConnector->connectBackwardVoid(selectedShapesProxy, SIGNAL(selectionChanged()), this, SLOT(slotUpdateAspectButton())); acyclicConnector->connectBackwardVoid(selectedShapesProxy, SIGNAL(selectionContentChanged()), this, SLOT(slotUpdateAspectButton())); KisAcyclicSignalConnector *sizeConnector = acyclicConnector->createCoordinatedConnector(); sizeConnector->connectForwardVoid(m_sizeAspectLocker.data(), SIGNAL(sliderValueChanged()), this, SLOT(slotResizeShapes())); sizeConnector->connectBackwardVoid(selectedShapesProxy, SIGNAL(selectionChanged()), this, SLOT(slotUpdateSizeBoxes())); KisAcyclicSignalConnector *contentSizeConnector = acyclicConnector->createCoordinatedConnector(); contentSizeConnector->connectBackwardVoid(selectedShapesProxy, SIGNAL(selectionContentChanged()), this, SLOT(slotUpdateSizeBoxesNoAspectChange())); // Connect and initialize anchor point resource KoCanvasResourceManager *resourceManager = m_tool->canvas()->resourceManager(); connect(resourceManager, SIGNAL(canvasResourceChanged(int,QVariant)), SLOT(resourceChanged(int,QVariant))); resourceManager->setResource(DefaultTool::HotPosition, int(KoFlake::AnchorPosition::Center)); positionSelector->setValue(KoFlake::AnchorPosition(resourceManager->resource(DefaultTool::HotPosition).toInt())); // Connect anchor point selector connect(positionSelector, SIGNAL(valueChanged(KoFlake::AnchorPosition)), SLOT(slotAnchorPointChanged())); dblOpacity->setRange(0.0, 1.0, 2); dblOpacity->setSingleStep(0.01); dblOpacity->setFastSliderStep(0.1); dblOpacity->setPrefixes(i18n("Opacity: "), i18n("Opacity [*varies*]: ")); dblOpacity->setValueGetter( [](KoShape *s) { return 1.0 - s->transparency(); } ); connect(dblOpacity, SIGNAL(valueChanged(qreal)), SLOT(slotOpacitySliderChanged(qreal))); // cold init slotUpdateOpacitySlider(); } DefaultToolGeometryWidget::~DefaultToolGeometryWidget() { } namespace { void tryAnchorPosition(KoFlake::AnchorPosition anchor, const QRectF &rect, QPointF *position) { bool valid = false; QPointF anchoredPosition = KoFlake::anchorToPoint(anchor, rect, &valid); if (valid) { *position = anchoredPosition; } } QRectF calculateSelectionBounds(KoSelection *selection, KoFlake::AnchorPosition anchor, bool useGlobalSize, QList *outShapes = 0) { QList shapes = selection->selectedEditableShapes(); KoShape *shape = shapes.size() == 1 ? shapes.first() : selection; QRectF resultRect = shape->outlineRect(); QPointF resultPoint = resultRect.topLeft(); tryAnchorPosition(anchor, resultRect, &resultPoint); if (useGlobalSize) { resultRect = shape->absoluteTransformation(0).mapRect(resultRect); } else { /** * Some shapes, e.g. KoSelection and KoShapeGroup don't have real size() and * do all the resizing with transformation(), just try to cover this case and * fetch their scale using the transform. */ KisAlgebra2D::DecomposedMatix matrix(shape->transformation()); resultRect = matrix.scaleTransform().mapRect(resultRect); } resultPoint = shape->absoluteTransformation(0).map(resultPoint); if (outShapes) { *outShapes = shapes; } return QRectF(resultPoint, resultRect.size()); } } void DefaultToolGeometryWidget::slotAnchorPointChanged() { if (!isVisible()) return; QVariant newValue(positionSelector->value()); m_tool->canvas()->resourceManager()->setResource(DefaultTool::HotPosition, newValue); slotUpdatePositionBoxes(); } void DefaultToolGeometryWidget::slotUpdateCheckboxes() { if (!isVisible()) return; KoSelection *selection = m_tool->canvas()->selectedShapesProxy()->selection(); QList shapes = selection->selectedEditableShapes(); KoShapeGroup *onlyGroupShape = 0; if (shapes.size() == 1) { onlyGroupShape = dynamic_cast(shapes.first()); } const bool uniformScalingAvailable = shapes.size() <= 1 && !onlyGroupShape; if (uniformScalingAvailable && !chkUniformScaling->isEnabled()) { chkUniformScaling->setChecked(m_savedUniformScaling); chkUniformScaling->setEnabled(uniformScalingAvailable); } else if (!uniformScalingAvailable && chkUniformScaling->isEnabled()) { m_savedUniformScaling = chkUniformScaling->isChecked(); chkUniformScaling->setChecked(true); chkUniformScaling->setEnabled(uniformScalingAvailable); } // TODO: not implemented yet! chkAnchorLock->setEnabled(false); } void DefaultToolGeometryWidget::slotAspectButtonToggled() { KoSelection *selection = m_tool->canvas()->selectedShapesProxy()->selection(); QList shapes = selection->selectedEditableShapes(); KUndo2Command *cmd = new KoShapeKeepAspectRatioCommand(shapes, aspectButton->keepAspectRatio()); m_tool->canvas()->addCommand(cmd); } void DefaultToolGeometryWidget::slotUpdateAspectButton() { if (!isVisible()) return; KoSelection *selection = m_tool->canvas()->selectedShapesProxy()->selection(); QList shapes = selection->selectedEditableShapes(); bool hasKeepAspectRatio = false; bool hasNotKeepAspectRatio = false; Q_FOREACH (KoShape *shape, shapes) { if (shape->keepAspectRatio()) { hasKeepAspectRatio = true; } else { hasNotKeepAspectRatio = true; } if (hasKeepAspectRatio && hasNotKeepAspectRatio) break; } Q_UNUSED(hasNotKeepAspectRatio); // TODO: use for tristated mode of the checkbox aspectButton->setKeepAspectRatio(hasKeepAspectRatio); } -namespace { -qreal calculateCommonShapeTransparency(const QList &shapes) -{ - qreal commonTransparency = -1.0; - - Q_FOREACH (KoShape *shape, shapes) { - if (commonTransparency < 0) { - commonTransparency = shape->transparency(); - } else if (!qFuzzyCompare(commonTransparency, shape->transparency())) { - commonTransparency = -1.0; - break; - } - } - - return commonTransparency; -} -} +//namespace { +//qreal calculateCommonShapeTransparency(const QList &shapes) +//{ +// qreal commonTransparency = -1.0; + +// Q_FOREACH (KoShape *shape, shapes) { +// if (commonTransparency < 0) { +// commonTransparency = shape->transparency(); +// } else if (!qFuzzyCompare(commonTransparency, shape->transparency())) { +// commonTransparency = -1.0; +// break; +// } +// } + +// return commonTransparency; +//} +//} void DefaultToolGeometryWidget::slotOpacitySliderChanged(qreal newOpacity) { KoSelection *selection = m_tool->canvas()->selectedShapesProxy()->selection(); QList shapes = selection->selectedEditableShapes(); if (shapes.isEmpty()) return; KUndo2Command *cmd = new KoShapeTransparencyCommand(shapes, 1.0 - newOpacity); m_tool->canvas()->addCommand(cmd); } void DefaultToolGeometryWidget::slotUpdateOpacitySlider() { if (!isVisible()) return; KoSelection *selection = m_tool->canvas()->selectedShapesProxy()->selection(); QList shapes = selection->selectedEditableShapes(); dblOpacity->setSelection(shapes); } void DefaultToolGeometryWidget::slotUpdateSizeBoxes(bool updateAspect) { if (!isVisible()) return; const bool useGlobalSize = chkGlobalCoordinates->isChecked(); const KoFlake::AnchorPosition anchor = positionSelector->value(); KoSelection *selection = m_tool->canvas()->selectedShapesProxy()->selection(); QRectF bounds = calculateSelectionBounds(selection, anchor, useGlobalSize); const bool hasSizeConfiguration = !bounds.isNull(); widthSpinBox->setEnabled(hasSizeConfiguration); heightSpinBox->setEnabled(hasSizeConfiguration); if (hasSizeConfiguration) { KisSignalsBlocker b(widthSpinBox, heightSpinBox); widthSpinBox->changeValue(bounds.width()); heightSpinBox->changeValue(bounds.height()); if (updateAspect) { m_sizeAspectLocker->updateAspect(); } } } void DefaultToolGeometryWidget::slotUpdateSizeBoxesNoAspectChange() { slotUpdateSizeBoxes(false); } void DefaultToolGeometryWidget::slotUpdatePositionBoxes() { if (!isVisible()) return; const bool useGlobalSize = chkGlobalCoordinates->isChecked(); const KoFlake::AnchorPosition anchor = positionSelector->value(); KoSelection *selection = m_tool->canvas()->selectedShapesProxy()->selection(); QRectF bounds = calculateSelectionBounds(selection, anchor, useGlobalSize); const bool hasSizeConfiguration = !bounds.isNull(); positionXSpinBox->setEnabled(hasSizeConfiguration); positionYSpinBox->setEnabled(hasSizeConfiguration); if (hasSizeConfiguration) { KisSignalsBlocker b(positionXSpinBox, positionYSpinBox); positionXSpinBox->changeValue(bounds.x()); positionYSpinBox->changeValue(bounds.y()); } } void DefaultToolGeometryWidget::slotRepositionShapes() { static const qreal eps = 1e-6; const bool useGlobalSize = chkGlobalCoordinates->isChecked(); const KoFlake::AnchorPosition anchor = positionSelector->value(); QList shapes; KoSelection *selection = m_tool->canvas()->selectedShapesProxy()->selection(); QRectF bounds = calculateSelectionBounds(selection, anchor, useGlobalSize, &shapes); if (bounds.isNull()) return; const QPointF oldPosition = bounds.topLeft(); const QPointF newPosition(positionXSpinBox->value(), positionYSpinBox->value()); const QPointF diff = newPosition - oldPosition; if (diff.manhattanLength() < eps) return; QList oldPositions; QList newPositions; Q_FOREACH (KoShape *shape, shapes) { const QPointF oldShapePosition = shape->absolutePosition(anchor); oldPositions << shape->absolutePosition(anchor); newPositions << oldShapePosition + diff; } KUndo2Command *cmd = new KoShapeMoveCommand(shapes, oldPositions, newPositions, anchor); m_tool->canvas()->addCommand(cmd); } void DefaultToolGeometryWidget::slotResizeShapes() { static const qreal eps = 1e-4; const bool useGlobalSize = chkGlobalCoordinates->isChecked(); const KoFlake::AnchorPosition anchor = positionSelector->value(); QList shapes; KoSelection *selection = m_tool->canvas()->selectedShapesProxy()->selection(); QRectF bounds = calculateSelectionBounds(selection, anchor, useGlobalSize, &shapes); if (bounds.isNull()) return; const QSizeF oldSize(bounds.size()); QSizeF newSize(widthSpinBox->value(), heightSpinBox->value()); newSize = KisAlgebra2D::ensureSizeNotSmaller(newSize, QSizeF(eps, eps)); const qreal scaleX = newSize.width() / oldSize.width(); const qreal scaleY = newSize.height() / oldSize.height(); if (qAbs(scaleX - 1.0) < eps && qAbs(scaleY - 1.0) < eps) return; const bool usePostScaling = shapes.size() > 1 || chkUniformScaling->isChecked(); KUndo2Command *cmd = new KoShapeResizeCommand(shapes, scaleX, scaleY, bounds.topLeft(), useGlobalSize, usePostScaling, selection->transformation()); m_tool->canvas()->addCommand(cmd); } void DefaultToolGeometryWidget::setUnit(const KoUnit &unit) { positionXSpinBox->setUnit(unit); positionYSpinBox->setUnit(unit); widthSpinBox->setUnit(unit); heightSpinBox->setUnit(unit); positionXSpinBox->setLineStep(1.0); positionYSpinBox->setLineStep(1.0); widthSpinBox->setLineStep(1.0); heightSpinBox->setLineStep(1.0); slotUpdatePositionBoxes(); slotUpdateSizeBoxes(); } bool DefaultToolGeometryWidget::useUniformScaling() const { return chkUniformScaling->isChecked(); } void DefaultToolGeometryWidget::showEvent(QShowEvent *event) { QWidget::showEvent(event); slotUpdatePositionBoxes(); slotUpdateSizeBoxes(); slotUpdateOpacitySlider(); slotUpdateAspectButton(); slotUpdateCheckboxes(); slotAnchorPointChanged(); } void DefaultToolGeometryWidget::resourceChanged(int key, const QVariant &res) { if (key == KoCanvasResourceManager::Unit) { setUnit(res.value()); } else if (key == DefaultTool::HotPosition) { positionSelector->setValue(KoFlake::AnchorPosition(res.toInt())); } } diff --git a/plugins/tools/tool_crop/kis_constrained_rect.cpp b/plugins/tools/tool_crop/kis_constrained_rect.cpp index 9f655ef4d0..e83b825944 100644 --- a/plugins/tools/tool_crop/kis_constrained_rect.cpp +++ b/plugins/tools/tool_crop/kis_constrained_rect.cpp @@ -1,400 +1,401 @@ /* * Copyright (c) 2014 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_constrained_rect.h" #include #include "kis_debug.h" #include "kis_algebra_2d.h" KisConstrainedRect::KisConstrainedRect() : m_centered(false), m_canGrow(true), m_ratio(1.0), m_widthLocked(false), m_heightLocked(false), m_ratioLocked(false) { } KisConstrainedRect::~KisConstrainedRect() { } void KisConstrainedRect::setRectInitial(const QRect &rect) { m_rect = rect; if (!ratioLocked()) { storeRatioSafe(m_rect.size()); } emit sigValuesChanged(); } void KisConstrainedRect::setCropRect(const QRect &cropRect) { m_cropRect = cropRect; } bool KisConstrainedRect::centered() const { return m_centered; } void KisConstrainedRect::setCentered(bool value) { m_centered = value; } bool KisConstrainedRect::canGrow() const { return m_canGrow; } void KisConstrainedRect::setCanGrow(bool value) { m_canGrow = value; } QRect KisConstrainedRect::rect() const { return m_rect.normalized(); } qreal KisConstrainedRect::ratio() const { return qAbs(m_ratio); } void KisConstrainedRect::moveHandle(HandleType handle, const QPoint &offset, const QRect &oldRect) { const QSize oldSize = oldRect.size(); QSize newSize = oldSize; QPoint newOffset = oldRect.topLeft(); int xSizeCoeff = 1; int ySizeCoeff = 1; qreal xOffsetFromSizeChange = 1.0; qreal yOffsetFromSizeChange = 1.0; int baseSizeCoeff = 1; bool useMoveOnly = false; switch (handle) { case UpperLeft: xSizeCoeff = -1; ySizeCoeff = -1; xOffsetFromSizeChange = -1.0; yOffsetFromSizeChange = -1.0; break; case UpperRight: xSizeCoeff = 1; ySizeCoeff = -1; xOffsetFromSizeChange = 0.0; yOffsetFromSizeChange = -1.0; break; case Creation: baseSizeCoeff = 0; + /* Falls through */ case LowerRight: xSizeCoeff = 1; ySizeCoeff = 1; xOffsetFromSizeChange = 0.0; yOffsetFromSizeChange = 0.0; break; case LowerLeft: xSizeCoeff = -1; ySizeCoeff = 1; xOffsetFromSizeChange = -1.0; yOffsetFromSizeChange = 0.0; break; case Upper: xSizeCoeff = 0; ySizeCoeff = -1; xOffsetFromSizeChange = -0.5; yOffsetFromSizeChange = -1.0; break; case Right: xSizeCoeff = 1; ySizeCoeff = 0; xOffsetFromSizeChange = 0.0; yOffsetFromSizeChange = -0.5; break; case Lower: xSizeCoeff = 0; ySizeCoeff = 1; xOffsetFromSizeChange = -0.5; yOffsetFromSizeChange = 0.0; break; case Left: xSizeCoeff = -1; ySizeCoeff = 0; xOffsetFromSizeChange = -1.0; yOffsetFromSizeChange = -0.5; break; case Inside: useMoveOnly = true; break; case None: // should never happen break; } if (!useMoveOnly) { const int centeringSizeCoeff = m_centered ? 2 : 1; if (m_centered) { xOffsetFromSizeChange = -0.5; yOffsetFromSizeChange = -0.5; } QSize sizeDiff(offset.x() * xSizeCoeff * centeringSizeCoeff, offset.y() * ySizeCoeff * centeringSizeCoeff); QSize tempSize = baseSizeCoeff * oldSize + sizeDiff; bool widthPreferrable = qAbs(tempSize.width()) > qAbs(tempSize.height() * m_ratio); if (ratioLocked() && ((widthPreferrable && xSizeCoeff != 0) || ySizeCoeff == 0)) { newSize.setWidth(tempSize.width()); newSize.setHeight(heightFromWidthUnsignedRatio(newSize.width(), m_ratio, tempSize.height())); } else if (ratioLocked() && ((!widthPreferrable && ySizeCoeff != 0) || xSizeCoeff == 0)) { newSize.setHeight(tempSize.height()); newSize.setWidth(widthFromHeightUnsignedRatio(newSize.height(), m_ratio, tempSize.width())); } else if (widthLocked() && heightLocked()) { newSize.setWidth(KisAlgebra2D::copysign(newSize.width(), tempSize.width())); newSize.setHeight(KisAlgebra2D::copysign(newSize.height(), tempSize.height())); } else if (widthLocked()) { newSize.setWidth(KisAlgebra2D::copysign(newSize.width(), tempSize.width())); newSize.setHeight(tempSize.height()); storeRatioSafe(newSize); } else if (heightLocked()) { newSize.setHeight(KisAlgebra2D::copysign(newSize.height(), tempSize.height())); newSize.setWidth(tempSize.width()); storeRatioSafe(newSize); } else { newSize = baseSizeCoeff * oldSize + sizeDiff; storeRatioSafe(newSize); } QSize realSizeDiff = newSize - baseSizeCoeff * oldSize; QPoint offsetDiff(realSizeDiff.width() * xOffsetFromSizeChange, realSizeDiff.height() * yOffsetFromSizeChange); newOffset = oldRect.topLeft() + offsetDiff; } else { newOffset = oldRect.topLeft() + offset; } m_rect = QRect(newOffset, newSize); if (!m_canGrow) { m_rect &= m_cropRect; } emit sigValuesChanged(); } QPointF KisConstrainedRect::handleSnapPoint(HandleType handle, const QPointF &cursorPos) { QPointF snapPoint = cursorPos; switch (handle) { case UpperLeft: snapPoint = m_rect.topLeft(); break; case UpperRight: snapPoint = m_rect.topRight() + QPointF(1, 0); break; case Creation: break; case LowerRight: snapPoint = m_rect.bottomRight() + QPointF(1, 1); break; case LowerLeft: snapPoint = m_rect.bottomLeft() + QPointF(0, 1); break; case Upper: snapPoint.ry() = m_rect.y(); break; case Right: snapPoint.rx() = m_rect.right() + 1; break; case Lower: snapPoint.ry() = m_rect.bottom() + 1; break; case Left: snapPoint.rx() = m_rect.x(); break; case Inside: break; case None: // should never happen break; } return snapPoint; } void KisConstrainedRect::normalize() { setRectInitial(m_rect.normalized()); } void KisConstrainedRect::setOffset(const QPoint &offset) { QRect newRect = m_rect; newRect.moveTo(offset); if (!m_canGrow) { newRect &= m_cropRect; } if (!newRect.isEmpty()) { m_rect = newRect; } emit sigValuesChanged(); } void KisConstrainedRect::setRatio(qreal value) { KIS_ASSERT_RECOVER_RETURN(value >= 0); const qreal eps = 1e-7; const qreal invEps = 1.0 / eps; if (value < eps || value > invEps) { emit sigValuesChanged(); return; } const QSize oldSize = m_rect.size(); QSize newSize = oldSize; if (widthLocked() && heightLocked()) { setHeightLocked(false); } m_ratio = value; if (!widthLocked() && !heightLocked()) { int area = oldSize.width() * oldSize.height(); newSize.setWidth(qRound(std::sqrt(area * m_ratio))); newSize.setHeight(qRound(newSize.width() / m_ratio)); } else if (widthLocked()) { newSize.setHeight(newSize.width() / m_ratio); } else if (heightLocked()) { newSize.setWidth(newSize.height() * m_ratio); } assignNewSize(newSize); } void KisConstrainedRect::setWidth(int value) { KIS_ASSERT_RECOVER_RETURN(value >= 0); const QSize oldSize = m_rect.size(); QSize newSize = oldSize; if (ratioLocked()) { newSize.setWidth(value); newSize.setHeight(newSize.width() / m_ratio); } else { newSize.setWidth(value); storeRatioSafe(newSize); } assignNewSize(newSize); } void KisConstrainedRect::setHeight(int value) { KIS_ASSERT_RECOVER_RETURN(value >= 0); const QSize oldSize = m_rect.size(); QSize newSize = oldSize; if (ratioLocked()) { newSize.setHeight(value); newSize.setWidth(newSize.height() * m_ratio); } else { newSize.setHeight(value); storeRatioSafe(newSize); } assignNewSize(newSize); } void KisConstrainedRect::assignNewSize(const QSize &newSize) { if (!m_centered) { m_rect.setSize(newSize); } else { QSize sizeDiff = newSize - m_rect.size(); m_rect.translate(-qRound(sizeDiff.width() / 2.0), -qRound(sizeDiff.height() / 2.0)); m_rect.setSize(newSize); } if (!m_canGrow) { m_rect &= m_cropRect; } emit sigValuesChanged(); } void KisConstrainedRect::storeRatioSafe(const QSize &newSize) { m_ratio = qAbs(qreal(newSize.width()) / newSize.height()); } int KisConstrainedRect::widthFromHeightUnsignedRatio(int height, qreal ratio, int oldWidth) const { int newWidth = qRound(height * ratio); return KisAlgebra2D::copysign(newWidth, oldWidth); } int KisConstrainedRect::heightFromWidthUnsignedRatio(int width, qreal ratio, int oldHeight) const { int newHeight = qRound(width / ratio); return KisAlgebra2D::copysign(newHeight, oldHeight); } bool KisConstrainedRect::widthLocked() const { return m_widthLocked; } bool KisConstrainedRect::heightLocked() const { return m_heightLocked; } bool KisConstrainedRect::ratioLocked() const { return m_ratioLocked; } void KisConstrainedRect::setWidthLocked(bool value) { m_widthLocked = value; m_ratioLocked &= !(m_widthLocked || m_heightLocked); emit sigLockValuesChanged(); } void KisConstrainedRect::setHeightLocked(bool value) { m_heightLocked = value; m_ratioLocked &= !(m_widthLocked || m_heightLocked); emit sigLockValuesChanged(); } void KisConstrainedRect::setRatioLocked(bool value) { m_ratioLocked = value; m_widthLocked &= !m_ratioLocked; m_heightLocked &= !m_ratioLocked; emit sigLockValuesChanged(); }