Skip to content

Commit 7c2504a

Browse files
Moving a Group (shape container) now moves all contained shapes.
OffsetX/Y, ExtentX/Y, Width and Height are always calculated to prevent desync between Group and contained shapes.
1 parent 8b75773 commit 7c2504a

File tree

3 files changed

+72
-48
lines changed

3 files changed

+72
-48
lines changed

docs/changes/1.2.0.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
## Enhancements
66

77
- `phpoffice/phpspreadsheet`: Allow version 1.9 or 2.0 by [@Progi1984](https://github.com/Progi1984) fixing [#790](https://github.com/PHPOffice/PHPPresentation/pull/790), [#812](https://github.com/PHPOffice/PHPPresentation/pull/812) in [#816](https://github.com/PHPOffice/PHPPresentation/pull/816)
8+
- Group Shape: moving the shape now moves all contained shapes. Offsets and size are calculated based on the contained shapes by [@DennisBirkholz](https://github.com/DennisBirkholz) in [#690](https://github.com/PHPOffice/PHPPresentation/pull/690)
89

910
## Bug fixes
1011

src/PhpPresentation/Shape/Group.php

+65-42
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,13 @@
2020
namespace PhpOffice\PhpPresentation\Shape;
2121

2222
use PhpOffice\PhpPresentation\AbstractShape;
23-
use PhpOffice\PhpPresentation\GeometryCalculator;
2423
use PhpOffice\PhpPresentation\ShapeContainerInterface;
2524
use PhpOffice\PhpPresentation\Traits\ShapeCollection;
2625

2726
class Group extends AbstractShape implements ShapeContainerInterface
2827
{
2928
use ShapeCollection;
3029

31-
/**
32-
* Extent X.
33-
*
34-
* @var int
35-
*/
36-
protected $extentX;
37-
38-
/**
39-
* Extent Y.
40-
*
41-
* @var int
42-
*/
43-
protected $extentY;
44-
4530
public function __construct()
4631
{
4732
parent::__construct();
@@ -52,22 +37,33 @@ public function __construct()
5237
*/
5338
public function getOffsetX(): int
5439
{
55-
if (empty($this->offsetX)) {
56-
$offsets = GeometryCalculator::calculateOffsets($this);
57-
$this->offsetX = $offsets[GeometryCalculator::X];
58-
$this->offsetY = $offsets[GeometryCalculator::Y];
40+
$offsetX = null;
41+
42+
foreach ($this->getShapeCollection() as $shape) {
43+
if ($offsetX === null) {
44+
$offsetX = $shape->getOffsetX();
45+
} else {
46+
$offsetX = \min($offsetX, $shape->getOffsetX());
47+
}
5948
}
6049

61-
return $this->offsetX;
50+
return $offsetX ?? 0;
6251
}
6352

6453
/**
65-
* Ignores setting the X Offset, preserving the default behavior.
54+
* Change the X offset by moving all contained shapes.
6655
*
6756
* @return $this
6857
*/
69-
public function setOffsetX(int $pValue = 0)
58+
public function setOffsetX(int $pValue = 0): self
7059
{
60+
$offsetX = $this->getOffsetX();
61+
$diff = $pValue - $offsetX;
62+
63+
foreach ($this->getShapeCollection() as $shape) {
64+
$shape->setOffsetX($shape->getOffsetX() + $diff);
65+
}
66+
7167
return $this;
7268
}
7369

@@ -76,22 +72,33 @@ public function setOffsetX(int $pValue = 0)
7672
*/
7773
public function getOffsetY(): int
7874
{
79-
if (empty($this->offsetY)) {
80-
$offsets = GeometryCalculator::calculateOffsets($this);
81-
$this->offsetX = $offsets[GeometryCalculator::X];
82-
$this->offsetY = $offsets[GeometryCalculator::Y];
75+
$offsetY = null;
76+
77+
foreach ($this->getShapeCollection() as $shape) {
78+
if ($offsetY === null) {
79+
$offsetY = $shape->getOffsetY();
80+
} else {
81+
$offsetY = \min($offsetY, $shape->getOffsetY());
82+
}
8383
}
8484

85-
return $this->offsetY;
85+
return $offsetY ?? 0;
8686
}
8787

8888
/**
89-
* Ignores setting the Y Offset, preserving the default behavior.
89+
* Change the Y offset by moving all contained shapes.
9090
*
9191
* @return $this
9292
*/
93-
public function setOffsetY(int $pValue = 0)
93+
public function setOffsetY(int $pValue = 0): self
9494
{
95+
$offsetY = $this->getOffsetY();
96+
$diff = $pValue - $offsetY;
97+
98+
foreach ($this->getShapeCollection() as $shape) {
99+
$shape->setOffsetY($shape->getOffsetY() + $diff);
100+
}
101+
95102
return $this;
96103
}
97104

@@ -100,35 +107,51 @@ public function setOffsetY(int $pValue = 0)
100107
*/
101108
public function getExtentX(): int
102109
{
103-
if (null === $this->extentX) {
104-
$extents = GeometryCalculator::calculateExtents($this);
105-
$this->extentX = $extents[GeometryCalculator::X] - $this->getOffsetX();
106-
$this->extentY = $extents[GeometryCalculator::Y] - $this->getOffsetY();
110+
$extentX = 0;
111+
112+
foreach ($this->getShapeCollection() as $shape) {
113+
$extentX = \max($extentX, $shape->getOffsetX() + $shape->getWidth());
107114
}
108115

109-
return $this->extentX;
116+
return $extentX - $this->getOffsetX();
110117
}
111118

112119
/**
113120
* Get Y Extent.
114121
*/
115122
public function getExtentY(): int
116123
{
117-
if (null === $this->extentY) {
118-
$extents = GeometryCalculator::calculateExtents($this);
119-
$this->extentX = $extents[GeometryCalculator::X] - $this->getOffsetX();
120-
$this->extentY = $extents[GeometryCalculator::Y] - $this->getOffsetY();
124+
$extentY = 0;
125+
126+
foreach ($this->getShapeCollection() as $shape) {
127+
$extentY = \max($extentY, $shape->getOffsetY() + $shape->getHeight());
121128
}
122129

123-
return $this->extentY;
130+
return $extentY - $this->getOffsetY();
131+
}
132+
133+
/**
134+
* Calculate the width based on the size/position of the contained shapes.
135+
*/
136+
public function getWidth(): int
137+
{
138+
return $this->getExtentX();
139+
}
140+
141+
/**
142+
* Calculate the height based on the size/position of the contained shapes.
143+
*/
144+
public function getHeight(): int
145+
{
146+
return $this->getExtentY();
124147
}
125148

126149
/**
127150
* Ignores setting the width, preserving the default behavior.
128151
*
129-
* @return self
152+
* @return $this
130153
*/
131-
public function setWidth(int $pValue = 0)
154+
public function setWidth(int $pValue = 0): self
132155
{
133156
return $this;
134157
}
@@ -138,7 +161,7 @@ public function setWidth(int $pValue = 0)
138161
*
139162
* @return $this
140163
*/
141-
public function setHeight(int $pValue = 0)
164+
public function setHeight(int $pValue = 0): self
142165
{
143166
return $this;
144167
}

tests/PhpPresentation/Tests/Shape/GroupTest.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ public function testOffsetX(): void
7979
$line1 = new Line(10, 20, 30, 50);
8080
$object->addShape($line1);
8181

82-
self::assertEquals(10, $object->getOffsetX());
82+
self::assertEquals($line1->getOffsetX(), $object->getOffsetX());
8383

84-
self::assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Group', $object->setOffsetX(mt_rand(1, 100)));
85-
self::assertEquals(10, $object->getOffsetX());
84+
self::assertInstanceOf(Group::class, $object->setOffsetX(mt_rand(1, 100)));
85+
self::assertEquals($line1->getOffsetX(), $object->getOffsetX());
8686
}
8787

8888
public function testOffsetY(): void
@@ -91,10 +91,10 @@ public function testOffsetY(): void
9191
$line1 = new Line(10, 20, 30, 50);
9292
$object->addShape($line1);
9393

94-
self::assertEquals(20, $object->getOffsetY());
94+
self::assertEquals($line1->getOffsetY(), $object->getOffsetY());
9595

96-
self::assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Group', $object->setOffsetY(mt_rand(1, 100)));
97-
self::assertEquals(20, $object->getOffsetY());
96+
self::assertInstanceOf(Group::class, $object->setOffsetY(mt_rand(1, 100)));
97+
self::assertEquals($line1->getOffsetY(), $object->getOffsetY());
9898
}
9999

100100
public function testExtentsAndOffsetsForOneShape(): void

0 commit comments

Comments
 (0)