Commit 89ba96d7 authored by C. Boemann's avatar C. Boemann

Support runaround distance individually on all 4 sides.

Treating them all as one gave layout issues
Related ui is updated too
parent 60a75f79
......@@ -97,7 +97,10 @@ KoShapePrivate::KoShapePrivate(KoShape *shape)
detectCollision(false),
protectContent(false),
textRunAroundSide(KoShape::BiggestRunAroundSide),
textRunAroundDistance(1.0),
textRunAroundDistanceTop(0.0),
textRunAroundDistanceLeft(0.0),
textRunAroundDistanceRight(0.0),
textRunAroundDistanceBottom(0.0),
textRunAroundThreshold(0.0),
textRunAroundContour(KoShape::ContourFull)
{
......@@ -916,16 +919,52 @@ void KoShape::setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runTh
d->shapeChanged(TextRunAroundChanged);
}
qreal KoShape::textRunAroundDistance() const
qreal KoShape::textRunAroundDistanceTop() const
{
Q_D(const KoShape);
return d->textRunAroundDistance;
return d->textRunAroundDistanceTop;
}
void KoShape::setTextRunAroundDistance(qreal distance)
void KoShape::setTextRunAroundDistanceTop(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistance = distance;
d->textRunAroundDistanceTop = distance;
}
qreal KoShape::textRunAroundDistanceLeft() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceLeft;
}
void KoShape::setTextRunAroundDistanceLeft(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceLeft = distance;
}
qreal KoShape::textRunAroundDistanceRight() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceRight;
}
void KoShape::setTextRunAroundDistanceRight(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceRight = distance;
}
qreal KoShape::textRunAroundDistanceBottom() const
{
Q_D(const KoShape);
return d->textRunAroundDistanceBottom;
}
void KoShape::setTextRunAroundDistanceBottom(qreal distance)
{
Q_D(KoShape);
d->textRunAroundDistanceBottom = distance;
}
qreal KoShape::textRunAroundThreshold() const
......@@ -1309,7 +1348,16 @@ QString KoShape::saveStyle(KoGenStyle &style, KoShapeSavingContext &context) con
break;
}
style.addPropertyPt("style:wrap-dynamic-threshold", textRunAroundThreshold(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin", textRunAroundDistance(), KoGenStyle::GraphicType);
if ((textRunAroundDistanceLeft() == textRunAroundDistanceRight())
&& (textRunAroundDistanceTop() == textRunAroundDistanceBottom())
&& (textRunAroundDistanceLeft() == textRunAroundDistanceTop())) {
style.addPropertyPt("fo:margin", textRunAroundDistanceLeft(), KoGenStyle::GraphicType);
} else {
style.addPropertyPt("fo:margin-left", textRunAroundDistanceLeft(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin-top", textRunAroundDistanceTop(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin-right", textRunAroundDistanceRight(), KoGenStyle::GraphicType);
style.addPropertyPt("fo:margin-bottom", textRunAroundDistanceBottom(), KoGenStyle::GraphicType);
}
return context.mainStyles().insert(style, context.isSet(KoShapeSavingContext::PresentationShape) ? "pr" : "gr");
}
......@@ -1342,15 +1390,29 @@ void KoShape::loadStyle(const KoXmlElement &element, KoShapeLoadingContext &cont
setContentProtected(protect.contains("content"));
QString margin = styleStack.property(KoXmlNS::fo, "margin");
if (margin.isEmpty())
margin = styleStack.property(KoXmlNS::fo, "margin-left");
if (margin.isEmpty())
margin = styleStack.property(KoXmlNS::fo, "margin-top");
if (margin.isEmpty())
margin = styleStack.property(KoXmlNS::fo, "margin-bottom");
if (margin.isEmpty())
margin = styleStack.property(KoXmlNS::fo, "margin-right");
setTextRunAroundDistance(KoUnit::parseValue(margin));
if (!margin.isEmpty()) {
setTextRunAroundDistanceLeft(KoUnit::parseValue(margin));
setTextRunAroundDistanceTop(KoUnit::parseValue(margin));
setTextRunAroundDistanceRight(KoUnit::parseValue(margin));
setTextRunAroundDistanceBottom(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-left");
if (!margin.isEmpty()) {
setTextRunAroundDistanceLeft(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-top");
if (!margin.isEmpty()) {
setTextRunAroundDistanceTop(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-right");
if (!margin.isEmpty()) {
setTextRunAroundDistanceRight(KoUnit::parseValue(margin));
}
margin = styleStack.property(KoXmlNS::fo, "margin-bottom");
if (!margin.isEmpty()) {
setTextRunAroundDistanceBottom(KoUnit::parseValue(margin));
}
QString wrap;
if (styleStack.hasProperty(KoXmlNS::style, "wrap")) {
......
......@@ -390,16 +390,52 @@ public:
void setTextRunAroundSide(TextRunAroundSide side, RunThroughLevel runThrough = Background);
/**
* The space between this shape's edge and text that runs around this shape.
* The space between this shape's left edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistance() const;
qreal textRunAroundDistanceLeft() const;
/**
* Set the space between this shape's edge and the text that run around this shape.
* Set the space between this shape's left edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistance(qreal distance);
void setTextRunAroundDistanceLeft(qreal distance);
/**
* The space between this shape's top edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceTop() const;
/**
* Set the space between this shape's top edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceTop(qreal distance);
/**
* The space between this shape's right edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceRight() const;
/**
* Set the space between this shape's right edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceRight(qreal distance);
/**
* The space between this shape's bottom edge and text that runs around this shape.
* @return the space around this shape to keep free from text
*/
qreal textRunAroundDistanceBottom() const;
/**
* Set the space between this shape's bottom edge and the text that run around this shape.
* @param distance the space around this shape to keep free from text
*/
void setTextRunAroundDistanceBottom(qreal distance);
/**
* Return the threshold above which text should flow around this shape.
......
......@@ -92,7 +92,10 @@ public:
int protectContent : 1;
KoShape::TextRunAroundSide textRunAroundSide;
qreal textRunAroundDistance;
qreal textRunAroundDistanceLeft;
qreal textRunAroundDistanceTop;
qreal textRunAroundDistanceRight;
qreal textRunAroundDistanceBottom;
qreal textRunAroundThreshold;
KoShape::TextRunAroundContour textRunAroundContour;
......
......@@ -26,16 +26,22 @@
class KoShapeRunAroundCommand::Private
{
public:
Private(KoShape *s, KoShape::TextRunAroundSide side, int runThrough, qreal distance, qreal threshold, KoShape::TextRunAroundContour contour)
Private(KoShape *s, KoShape::TextRunAroundSide side, int runThrough, qreal distanceLeft, qreal distanceTop, qreal distanceRight, qreal distanceBottom, qreal threshold, KoShape::TextRunAroundContour contour)
: shape(s)
, newSide(side)
, newRunThrough(runThrough)
, newDistance(distance)
, newDistanceLeft(distanceLeft)
, newDistanceTop(distanceTop)
, newDistanceRight(distanceRight)
, newDistanceBottom(distanceBottom)
, newThreshold(threshold)
, newContour(contour)
, oldSide(shape->textRunAroundSide())
, oldRunThrough(shape->runThrough())
, oldDistance(shape->textRunAroundDistance())
, oldDistanceLeft(shape->textRunAroundDistanceLeft())
, oldDistanceTop(shape->textRunAroundDistanceTop())
, oldDistanceRight(shape->textRunAroundDistanceRight())
, oldDistanceBottom(shape->textRunAroundDistanceBottom())
, oldThreshold(shape->textRunAroundThreshold())
, oldContour(shape->textRunAroundContour())
{}
......@@ -43,19 +49,25 @@ public:
KoShape *shape;
KoShape::TextRunAroundSide newSide;
int newRunThrough;
qreal newDistance;
qreal newDistanceLeft;
qreal newDistanceTop;
qreal newDistanceRight;
qreal newDistanceBottom;
qreal newThreshold;
KoShape::TextRunAroundContour newContour;
KoShape::TextRunAroundSide oldSide;
int oldRunThrough;
qreal oldDistance;
qreal oldDistanceLeft;
qreal oldDistanceTop;
qreal oldDistanceRight;
qreal oldDistanceBottom;
qreal oldThreshold;
KoShape::TextRunAroundContour oldContour;
};
KoShapeRunAroundCommand::KoShapeRunAroundCommand(KoShape *shape, KoShape::TextRunAroundSide side, int runThrough, qreal distance, qreal threshold, KoShape::TextRunAroundContour contour, KUndo2Command *parent)
KoShapeRunAroundCommand::KoShapeRunAroundCommand(KoShape *shape, KoShape::TextRunAroundSide side, int runThrough, qreal distanceLeft, qreal distanceTop, qreal distanceRight, qreal distanceBottom, qreal threshold, KoShape::TextRunAroundContour contour, KUndo2Command *parent)
: KUndo2Command(parent)
, d(new Private(shape, side, runThrough, distance, threshold, contour))
, d(new Private(shape, side, runThrough, distanceLeft, distanceTop, distanceRight, distanceBottom, threshold, contour))
{
setText(i18nc("(qtundo-format)", "Change Shape RunAround"));
}
......@@ -70,7 +82,10 @@ void KoShapeRunAroundCommand::redo()
KUndo2Command::redo();
d->shape->setTextRunAroundSide(d->newSide, KoShape::Background);
d->shape->setRunThrough(d->newRunThrough);
d->shape->setTextRunAroundDistance(d->newDistance);
d->shape->setTextRunAroundDistanceLeft(d->newDistanceLeft);
d->shape->setTextRunAroundDistanceTop(d->newDistanceTop);
d->shape->setTextRunAroundDistanceRight(d->newDistanceRight);
d->shape->setTextRunAroundDistanceBottom(d->newDistanceBottom);
d->shape->setTextRunAroundThreshold(d->newThreshold);
d->shape->setTextRunAroundContour(d->newContour);
d->shape->notifyChanged();
......@@ -81,7 +96,10 @@ void KoShapeRunAroundCommand::undo()
KUndo2Command::undo();
d->shape->setTextRunAroundSide(d->oldSide, KoShape::Background);
d->shape->setRunThrough(d->oldRunThrough);
d->shape->setTextRunAroundDistance(d->oldDistance);
d->shape->setTextRunAroundDistanceLeft(d->oldDistanceLeft);
d->shape->setTextRunAroundDistanceTop(d->oldDistanceTop);
d->shape->setTextRunAroundDistanceRight(d->oldDistanceRight);
d->shape->setTextRunAroundDistanceBottom(d->oldDistanceBottom);
d->shape->setTextRunAroundThreshold(d->oldThreshold);
d->shape->setTextRunAroundContour(d->oldContour);
d->shape->notifyChanged();
......
......@@ -29,7 +29,7 @@
class FLAKE_EXPORT KoShapeRunAroundCommand : public KUndo2Command
{
public:
KoShapeRunAroundCommand(KoShape *shape, KoShape::TextRunAroundSide side, int runThrough, qreal distance, qreal threshold, KoShape::TextRunAroundContour contour, KUndo2Command *parent = 0);
KoShapeRunAroundCommand(KoShape *shape, KoShape::TextRunAroundSide side, int runThrough, qreal distanceLeft, qreal distanceTop, qreal distanceRight, qreal distanceBottom, qreal threshold, KoShape::TextRunAroundContour contour, KUndo2Command *parent = 0);
virtual ~KoShapeRunAroundCommand();
/// redo the command
......
......@@ -38,13 +38,16 @@ KoTextLayoutObstruction::KoTextLayoutObstruction(KoShape *shape, const QTransfor
QPainterPath path = decoratedOutline(m_shape, borderHalfWidth);
//TODO check if path is convex. otherwise do triangulation and create more convex obstructions
init(matrix, path, shape->textRunAroundDistance(), borderHalfWidth);
init(matrix, path, shape->textRunAroundDistanceLeft(), shape->textRunAroundDistanceTop(), shape->textRunAroundDistanceRight(), shape->textRunAroundDistanceBottom(), borderHalfWidth);
if (shape->textRunAroundSide() == KoShape::NoRunAround) {
// make the shape take the full width of the text area
m_side = Empty;
} else if (shape->textRunAroundSide() == KoShape::RunThrough) {
m_distance = 0;
m_distanceLeft = 0;
m_distanceTop = 0;
m_distanceRight = 0;
m_distanceBottom = 0;
// We don't exist.
return;
} else if (shape->textRunAroundSide() == KoShape::LeftRunAroundSide) {
......@@ -74,7 +77,7 @@ KoTextLayoutObstruction::KoTextLayoutObstruction(QRectF rect, bool rtl)
QPainterPath path;
path.addRect(rect);
init(QTransform(), path, textRunAroundDistance, borderHalfWidth);
init(QTransform(), path, textRunAroundDistance, 0.0, textRunAroundDistance, 0.0, borderHalfWidth);
if (rtl) {
m_side = Right;
} else {
......@@ -134,22 +137,45 @@ QPainterPath KoTextLayoutObstruction::decoratedOutline(const KoShape *shape, qre
return path;
}
void KoTextLayoutObstruction::init(const QTransform &matrix, const QPainterPath &obstruction, qreal distance, qreal borderHalfWidth)
void KoTextLayoutObstruction::init(const QTransform &matrix, const QPainterPath &obstruction, qreal distanceLeft, qreal distanceTop, qreal distanceRight, qreal distanceBottom, qreal borderHalfWidth)
{
m_distance = distance;
m_distanceLeft = distanceLeft;
m_distanceTop = distanceTop;
m_distanceRight = distanceRight;
m_distanceBottom = distanceBottom;
QPainterPath path = matrix.map(obstruction);
m_bounds = path.boundingRect();
distance += borderHalfWidth;
if (distance > 0.0) {
QPainterPathStroker stroker;
stroker.setWidth(2 * distance);
stroker.setJoinStyle(Qt::MiterJoin);
stroker.setCapStyle(Qt::SquareCap);
path = stroker.createStroke(path) + path;
m_bounds = path.boundingRect();
}
distanceLeft += borderHalfWidth;
distanceTop += borderHalfWidth;
distanceRight += borderHalfWidth;
distanceBottom += borderHalfWidth;
// Let's extend the outline with at least the border half width in all directions.
// However since the distance can be express in 4 directions and QPainterPathStroker only
// handles a penWidth we do some tricks to get it working.
//
// Explaination in one dimension only: we sum the distances top and below and use that as the
// penWidth. afterwards we translate the result so it is destributed correctly by top and bottom
// Now by doing that we would also implicitly set the left+right size of the pen which is no good,
// so in order to set that to a minimal value (we choose 1, as 0 would give division by 0) we do
// the following:. We scale the original path by sumX, stroke the path with penwidth=sumY, then
// scale it back. Effectively we have now stroked with a pen sized 1 x sumY.
//
// The math to do both x an y in one go becomes a little more complex, but only a little.
qreal extraWidth = qMax(qreal(1.0), distanceLeft + distanceRight);
qreal extraHeight = qMax(qreal(1.0), distanceTop + distanceBottom);
QPainterPathStroker stroker;
stroker.setWidth(extraWidth);
stroker.setJoinStyle(Qt::MiterJoin);
stroker.setCapStyle(Qt::SquareCap);
QPainterPath bigPath = stroker.createStroke(QTransform().scale(1.0, extraWidth / extraHeight).map(path));
bigPath = QTransform().scale(1.0, extraHeight / extraWidth). map(bigPath);
path += bigPath.translated(extraWidth / 2 - distanceLeft, extraHeight / 2 - distanceTop);
m_bounds = path.boundingRect();
// Now we need to change the path into a polygon for easier handling later on
m_polygon = path.toFillPolygon();
QPointF prev = *(m_polygon.begin());
foreach (const QPointF &vtx, m_polygon) { //initialized edges
......@@ -179,7 +205,7 @@ void KoTextLayoutObstruction::changeMatrix(const QTransform &matrix)
qreal borderHalfWidth;
QPainterPath path = decoratedOutline(m_shape, borderHalfWidth);
init(matrix, path, m_distance, borderHalfWidth);
init(matrix, path, m_distanceLeft, m_distanceTop, m_distanceRight, m_distanceBottom, borderHalfWidth);
}
QRectF KoTextLayoutObstruction::cropToLine(const QRectF &lineRect)
......
......@@ -37,7 +37,7 @@ public:
KoTextLayoutObstruction(QRectF rect, bool rtl);
void init(const QTransform &matrix, const QPainterPath &obstruction, qreal distance, qreal borderHalfWidth);
void init(const QTransform &matrix, const QPainterPath &obstruction, qreal distanceLeft, qreal distanceTop, qreal distanceRight, qreal distanceBottom, qreal borderHalfWidth);
QRectF limit(const QRectF &content);
......@@ -81,7 +81,10 @@ private:
QMultiMap<qreal, QLineF> m_edges; //sorted with y-coord
KoShape *m_shape;
QRectF m_rect;
qreal m_distance;
qreal m_distanceLeft;
qreal m_distanceTop;
qreal m_distanceRight;
qreal m_distanceBottom;
qreal m_borderHalfWidth;
qreal m_runAroundThreshold;
};
......
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