Commit 1d6e688c authored by Tusooa Zhu's avatar Tusooa Zhu 🅱

Port aligning and distributing shapes to new strokes

parent 7a9350d7
......@@ -762,6 +762,151 @@ void KoFlake::GroupShapes::ungroupShapes(KoShapeContainer* container, QList<KoSh
}
}
void KoFlake::alignShapes(const QList<KoShape *>& shapes, KoFlake::Align align, const QRectF& boundingRect)
{
QPointF position;
QPointF delta;
QRectF bRect;
Q_FOREACH (KoShape *shape, shapes) {
position = shape->absolutePosition();
bRect = shape->absoluteOutlineRect();
switch (align) {
case HorizontalLeftAlignment:
delta = QPointF(boundingRect.left(), bRect.y()) - bRect.topLeft();
break;
case HorizontalCenterAlignment:
delta = QPointF(boundingRect.center().x() - bRect.width() / 2, bRect.y()) - bRect.topLeft();
break;
case HorizontalRightAlignment:
delta = QPointF(boundingRect.right() - bRect.width(), bRect.y()) - bRect.topLeft();
break;
case VerticalTopAlignment:
delta = QPointF(bRect.x(), boundingRect.top()) - bRect.topLeft();
break;
case VerticalCenterAlignment:
delta = QPointF(bRect.x(), boundingRect.center().y() - bRect.height() / 2) - bRect.topLeft();
break;
case VerticalBottomAlignment:
delta = QPointF(bRect.x(), boundingRect.bottom() - bRect.height()) - bRect.topLeft();
break;
};
moveShapes({shape}, delta);
}
}
static qreal getAvailableSpace(KoShape *first, KoShape *last, qreal extent, const QRectF &boundingRect, KoFlake::Distribute distribute)
{
using namespace KoFlake;
switch (distribute) {
case HorizontalCenterDistribution:
return boundingRect.width() - last->absoluteOutlineRect().width() / 2 - first->absoluteOutlineRect().width() / 2;
break;
case HorizontalGapsDistribution:
return boundingRect.width() - extent;
break;
case HorizontalLeftDistribution:
return boundingRect.width() - last->absoluteOutlineRect().width();
break;
case HorizontalRightDistribution:
return boundingRect.width() - first->absoluteOutlineRect().width();
break;
case VerticalCenterDistribution:
return boundingRect.height() - last->absoluteOutlineRect().height() / 2 - first->absoluteOutlineRect().height() / 2;
break;
case VerticalGapsDistribution:
return boundingRect.height() - extent;
break;
case VerticalBottomDistribution:
return boundingRect.height() - first->absoluteOutlineRect().height();
break;
case VerticalTopDistribution:
return boundingRect.height() - last->absoluteOutlineRect().height();
break;
}
return 0.0;
}
void KoFlake::distributeShapes(const QList<KoShape *>& shapes, KoFlake::Distribute distribute, const QRectF& boundingRect)
{
QMap<qreal, KoShape*> sortedPos;
QRectF bRect;
qreal extent = 0.0;
// sort by position and calculate sum of objects width/height
Q_FOREACH (KoShape *shape, shapes) {
bRect = shape->absoluteOutlineRect();
switch (distribute) {
case HorizontalCenterDistribution:
sortedPos[bRect.center().x()] = shape;
break;
case HorizontalGapsDistribution:
case HorizontalLeftDistribution:
sortedPos[bRect.left()] = shape;
extent += bRect.width();
break;
case HorizontalRightDistribution:
sortedPos[bRect.right()] = shape;
break;
case VerticalCenterDistribution:
sortedPos[bRect.center().y()] = shape;
break;
case VerticalGapsDistribution:
case VerticalBottomDistribution:
sortedPos[bRect.bottom()] = shape;
extent += bRect.height();
break;
case VerticalTopDistribution:
sortedPos[bRect.top()] = shape;
break;
}
}
KoShape* first = sortedPos.begin().value();
KoShape* last = (--sortedPos.end()).value();
// determine the available space to distribute
qreal space = getAvailableSpace(first, last, extent, boundingRect, distribute);
qreal pos = 0.0, step = space / qreal(shapes.count() - 1);
QList<QPointF> previousPositions;
QList<QPointF> newPositions;
QPointF position;
QPointF delta;
QMapIterator<qreal, KoShape*> it(sortedPos);
while (it.hasNext()) {
it.next();
position = it.value()->absolutePosition();
bRect = it.value()->absoluteOutlineRect();
switch (distribute) {
case HorizontalCenterDistribution:
delta = QPointF(boundingRect.x() + first->absoluteOutlineRect().width() / 2 + pos - bRect.width() / 2, bRect.y()) - bRect.topLeft();
break;
case HorizontalGapsDistribution:
delta = QPointF(boundingRect.left() + pos, bRect.y()) - bRect.topLeft();
pos += bRect.width();
break;
case HorizontalLeftDistribution:
delta = QPointF(boundingRect.left() + pos, bRect.y()) - bRect.topLeft();
break;
case HorizontalRightDistribution:
delta = QPointF(boundingRect.left() + first->absoluteOutlineRect().width() + pos - bRect.width(), bRect.y()) - bRect.topLeft();
break;
case VerticalCenterDistribution:
delta = QPointF(bRect.x(), boundingRect.y() + first->absoluteOutlineRect().height() / 2 + pos - bRect.height() / 2) - bRect.topLeft();
break;
case VerticalGapsDistribution:
delta = QPointF(bRect.x(), boundingRect.top() + pos) - bRect.topLeft();
pos += bRect.height();
break;
case VerticalBottomDistribution:
delta = QPointF(bRect.x(), boundingRect.top() + first->absoluteOutlineRect().height() + pos - bRect.height()) - bRect.topLeft();
break;
case VerticalTopDistribution:
delta = QPointF(bRect.x(), boundingRect.top() + pos) - bRect.topLeft();
break;
};
moveShapes({it.value()}, delta);
pos += step;
}
}
QDebug operator<<(QDebug dbg, const KoFlake::ReorderShapes::IndexedShape &indexedShape)
{
......
......@@ -252,6 +252,44 @@ namespace KoFlake
QList<KoShape*> topLevelShapes = QList<KoShape*>());
}
/// from KoShapeAlignCommand
enum Align {
HorizontalLeftAlignment, ///< Align left
HorizontalCenterAlignment, ///< Align Centered horizontally
HorizontalRightAlignment, ///< Align Right
VerticalBottomAlignment, ///< Align bottom
VerticalCenterAlignment, ///< Align centered vertically
VerticalTopAlignment ///< Align top
};
/**
* Align a set of shapes in a rect
* @param shapes a set of all the shapes that should be aligned
* @param align the alignment type
* @param boundingRect the rect the shape will be aligned in
*/
KRITAFLAKE_EXPORT void alignShapes(const QList<KoShape*> &shapes, Align align, const QRectF &boundingRect);
/// from KoShapeDistributeCommand
enum Distribute {
HorizontalCenterDistribution, ///< Horizontal centered
HorizontalGapsDistribution, ///< Horizontal Gaps
HorizontalLeftDistribution, ///< Horizontal Left
HorizontalRightDistribution, ///< Horizontal Right
VerticalCenterDistribution, ///< Vertical centered
VerticalGapsDistribution, ///< Vertical Gaps
VerticalBottomDistribution, ///< Vertical bottom
VerticalTopDistribution ///< Vertical top
};
/**
* Distribute a set of shapes in a rect
* @param shapes a set of all the shapes that should be distributed
* @param distribute the distribution type
* @param boundingRect the rect the shapes will be distributed in
*/
KRITAFLAKE_EXPORT void distributeShapes(const QList<KoShape*> &shapes, Distribute distribute, const QRectF &boundingRect);
}
KRITAFLAKE_EXPORT QDebug operator<<(QDebug dbg, const KoFlake::ReorderShapes::IndexedShape &indexedShape);
......
......@@ -45,15 +45,6 @@
#include <KoCanvasBase.h>
#include <KoCanvasResourceProvider.h>
#include <KoShapeRubberSelectStrategy.h>
#include <commands/KoShapeMoveCommand.h>
#include <commands/KoShapeTransformCommand.h>
#include <commands/KoShapeDeleteCommand.h>
#include <commands/KoShapeCreateCommand.h>
#include <commands/KoShapeGroupCommand.h>
#include <commands/KoShapeUngroupCommand.h>
#include <commands/KoShapeDistributeCommand.h>
#include <commands/KoKeepShapesSelectedCommand.h>
#include <commands/KisShapeLayerCommands.h>
#include <KoSnapGuide.h>
#include <KoStrokeConfigWidget.h>
#include "kis_action_registry.h"
......@@ -391,24 +382,24 @@ void DefaultTool::setupActions()
{
m_alignSignalsMapper = new QSignalMapper(this);
addMappedAction(m_alignSignalsMapper, "object_align_horizontal_left", KoShapeAlignCommand::HorizontalLeftAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_horizontal_center", KoShapeAlignCommand::HorizontalCenterAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_horizontal_right", KoShapeAlignCommand::HorizontalRightAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_vertical_top", KoShapeAlignCommand::VerticalTopAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_vertical_center", KoShapeAlignCommand::VerticalCenterAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_vertical_bottom", KoShapeAlignCommand::VerticalBottomAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_horizontal_left", KoFlake::HorizontalLeftAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_horizontal_center", KoFlake::HorizontalCenterAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_horizontal_right", KoFlake::HorizontalRightAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_vertical_top", KoFlake::VerticalTopAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_vertical_center", KoFlake::VerticalCenterAlignment);
addMappedAction(m_alignSignalsMapper, "object_align_vertical_bottom", KoFlake::VerticalBottomAlignment);
m_distributeSignalsMapper = new QSignalMapper(this);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_left", KoShapeDistributeCommand::HorizontalLeftDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_center", KoShapeDistributeCommand::HorizontalCenterDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_right", KoShapeDistributeCommand::HorizontalRightDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_gaps", KoShapeDistributeCommand::HorizontalGapsDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_left", KoFlake::HorizontalLeftDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_center", KoFlake::HorizontalCenterDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_right", KoFlake::HorizontalRightDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_horizontal_gaps", KoFlake::HorizontalGapsDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_top", KoShapeDistributeCommand::VerticalTopDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_center", KoShapeDistributeCommand::VerticalCenterDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_bottom", KoShapeDistributeCommand::VerticalBottomDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_gaps", KoShapeDistributeCommand::VerticalGapsDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_top", KoFlake::VerticalTopDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_center", KoFlake::VerticalCenterDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_bottom", KoFlake::VerticalBottomDistribution);
addMappedAction(m_distributeSignalsMapper, "object_distribute_vertical_gaps", KoFlake::VerticalGapsDistribution);
m_transformSignalsMapper = new QSignalMapper(this);
......@@ -1355,8 +1346,8 @@ void DefaultTool::selectionSplitShapes()
void DefaultTool::selectionAlign(int _align)
{
KoShapeAlignCommand::Align align =
static_cast<KoShapeAlignCommand::Align>(_align);
KoFlake::Align align =
static_cast<KoFlake::Align>(_align);
KoSelection *selection = koSelection();
if (!selection) return;
......@@ -1381,14 +1372,18 @@ void DefaultTool::selectionAlign(int _align)
bb = KoShape::absoluteOutlineRect(editableShapes);
}
KoShapeAlignCommand *cmd = new KoShapeAlignCommand(editableShapes, align, bb);
canvas()->addCommand(cmd);
canvas()->strokeHelper()->run(kundo2_i18n("Align shapes (new)"),
[editableShapes, align, bb]() {
KoFlake::alignShapes(editableShapes, align, bb);
return true;
}
);
}
void DefaultTool::selectionDistribute(int _distribute)
{
KoShapeDistributeCommand::Distribute distribute =
static_cast<KoShapeDistributeCommand::Distribute>(_distribute);
KoFlake::Distribute distribute =
static_cast<KoFlake::Distribute>(_distribute);
KoSelection *selection = koSelection();
if (!selection) return;
......@@ -1399,8 +1394,11 @@ void DefaultTool::selectionDistribute(int _distribute)
}
QRectF bb = KoShape::absoluteOutlineRect(editableShapes);
KoShapeDistributeCommand *cmd = new KoShapeDistributeCommand(editableShapes, distribute, bb);
canvas()->addCommand(cmd);
canvas()->strokeHelper()->run(kundo2_i18n("Distribute shapes (new)"),
[editableShapes, distribute, bb]() {
KoFlake::distributeShapes(editableShapes, distribute, bb);
return true;
});
}
void DefaultTool::selectionBringToFront()
......@@ -1435,11 +1433,11 @@ void DefaultTool::selectionReorder(KoFlake::ReorderShapes::MoveShapeType order)
return;
}
KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2 *>(canvas());
KoShapeManager *manager = shapeManager();
KisNodeReplaceBasedStrokeStrategy::runInStrokeOnce(kisCanvas->image().data(),
kisCanvas, kundo2_i18n("Reorder shapes (new)"),
[selectedShapes, manager, order]() { return KoFlake::ReorderShapes::doReordering(selectedShapes, manager, order) ? KisNodeReplaceBasedStrokeStrategy::COMMAND_NEEDED : KisNodeReplaceBasedStrokeStrategy::COMMAND_UNNEEDED; });
canvas()->strokeHelper()->run(kundo2_i18n("Reorder shapes (new)"),
[selectedShapes, manager, order]() {
return KoFlake::ReorderShapes::doReordering(selectedShapes, manager, order);
});
}
QList<QPointer<QWidget> > DefaultTool::createOptionWidgets()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment