diff --git a/examples/basic-centered.php b/examples/basic-centered.php new file mode 100644 index 00000000..cdac52c7 --- /dev/null +++ b/examples/basic-centered.php @@ -0,0 +1,25 @@ +getSelectedItem()->getText(); +}; + +$menu = (new CliMenuBuilder) + ->setTitle('Basic CLI Menu') + ->addItem('First Item', $itemCallable) + ->addItem('Make menu wider', function (CliMenu $menu) { + $menu->getStyle()->setWidth($menu->getStyle()->getWidth() + 10); + $menu->redraw(); + }) + ->addItem('Third Item', $itemCallable) + ->addLineBreak('-') + ->setWidth(70) + ->setMarginAuto() + ->build(); + +$menu->open(); diff --git a/src/CliMenuBuilder.php b/src/CliMenuBuilder.php index 3a2174b6..8bcc742d 100644 --- a/src/CliMenuBuilder.php +++ b/src/CliMenuBuilder.php @@ -230,8 +230,18 @@ public function setPadding(int $padding) : self return $this; } + public function setMarginAuto() : self + { + $this->style['marginAuto'] = true; + + return $this; + } + public function setMargin(int $margin) : self { + Assertion::greaterOrEqualThan($margin, 0); + + $this->style['marginAuto'] = false; $this->style['margin'] = $margin; return $this; @@ -320,17 +330,20 @@ private function getMenuStyle() : MenuStyle private function buildStyle() : MenuStyle { - return (new MenuStyle($this->terminal)) + $style = (new MenuStyle($this->terminal)) ->setFg($this->style['fg']) ->setBg($this->style['bg']) ->setWidth($this->style['width']) ->setPadding($this->style['padding']) - ->setMargin($this->style['margin']) ->setSelectedMarker($this->style['selectedMarker']) ->setUnselectedMarker($this->style['unselectedMarker']) ->setItemExtra($this->style['itemExtra']) ->setDisplaysExtra($this->style['displaysExtra']) ->setTitleSeparator($this->style['titleSeparator']); + + $this->style['marginAuto'] ? $style->setMarginAuto() : $style->setMargin($this->style['margin']); + + return $style; } /** diff --git a/src/Dialogue/Dialogue.php b/src/Dialogue/Dialogue.php index f9278222..aa0e343a 100644 --- a/src/Dialogue/Dialogue.php +++ b/src/Dialogue/Dialogue.php @@ -74,7 +74,7 @@ protected function calculateCoordinates() : void //x $parentStyle = $this->parentMenu->getStyle(); $dialogueHalfLength = (mb_strlen($this->text) + ($this->style->getPadding() * 2)) / 2; - $widthHalfLength = ceil($parentStyle->getWidth() / 2); + $widthHalfLength = ceil($parentStyle->getWidth() / 2 + $parentStyle->getMargin()); $this->x = $widthHalfLength - $dialogueHalfLength; } diff --git a/src/Input/InputIO.php b/src/Input/InputIO.php index ae655cfd..d770c71d 100644 --- a/src/Input/InputIO.php +++ b/src/Input/InputIO.php @@ -132,7 +132,7 @@ private function calculateXPosition(Input $input, string $userInput) : int $parentStyle = $this->parentMenu->getStyle(); $halfWidth = ($width + ($input->getStyle()->getPadding() * 2)) / 2; - $parentHalfWidth = ceil($parentStyle->getWidth() / 2); + $parentHalfWidth = ceil($parentStyle->getWidth() / 2 + $parentStyle->getMargin()); return $parentHalfWidth - $halfWidth; } diff --git a/src/MenuStyle.php b/src/MenuStyle.php index 9f630719..c0a9493f 100644 --- a/src/MenuStyle.php +++ b/src/MenuStyle.php @@ -73,6 +73,11 @@ class MenuStyle */ private $titleSeparator; + /** + * @var bool + */ + private $marginAuto = false; + /** * Default Values * @@ -89,6 +94,7 @@ class MenuStyle 'itemExtra' => '✔', 'displaysExtra' => false, 'titleSeparator' => '=', + 'marginAuto' => false, ]; public static function getDefaultStyleValues() : array @@ -233,7 +239,7 @@ public function getUnselectedUnsetCode() : string */ protected function calculateContentWidth() : void { - $this->contentWidth = $this->width - ($this->padding*2) - ($this->margin*2); + $this->contentWidth = $this->width - ($this->padding * 2); } public function getFg() : string @@ -267,13 +273,14 @@ public function getWidth() : int public function setWidth(int $width) : self { - $availableWidth = $this->terminal->getWidth() - ($this->margin * 2) - ($this->padding * 2); - - if ($width >= $availableWidth) { - $width = $availableWidth; + if ($width >= $this->terminal->getWidth()) { + $width = $this->terminal->getWidth(); } $this->width = $width; + if ($this->marginAuto) { + $this->setMarginAuto(); + } $this->calculateContentWidth(); return $this; @@ -298,12 +305,19 @@ public function getMargin() : int return $this->margin; } + public function setMarginAuto() : self + { + $this->marginAuto = true; + $this->margin = floor(($this->terminal->getWidth() - $this->width) / 2); + + return $this; + } + public function setMargin(int $margin) : self { + $this->marginAuto = false; $this->margin = $margin; - $this->calculateContentWidth(); - return $this; } diff --git a/test/CliMenuBuilderTest.php b/test/CliMenuBuilderTest.php index af096b2c..8cf6bc7d 100644 --- a/test/CliMenuBuilderTest.php +++ b/test/CliMenuBuilderTest.php @@ -437,6 +437,93 @@ public function testThrowsExceptionWhenDisablingRootMenu() : void (new CliMenuBuilder)->disableMenu(); } + + /** + * @dataProvider marginBelowZeroProvider + */ + public function testSetMarginThrowsExceptionIfValueIsNotZeroOrAbove(int $value) : void + { + self::expectException(\Assert\InvalidArgumentException::class); + + + (new CliMenuBuilder)->setMargin($value); + } + + public function marginBelowZeroProvider() : array + { + return [[-1], [-2], [-10]]; + } + + /** + * @dataProvider marginAboveZeroProvider + */ + public function testSetMarginAcceptsZeroAndPositiveIntegers(int $value) : void + { + $menu = (new CliMenuBuilder)->setMargin($value)->build(); + + self::assertSame($value, $menu->getStyle()->getMargin()); + } + + public function marginAboveZeroProvider() : array + { + return [[0], [1], [10], [50]]; + } + + public function testSetMarginAutoAutomaticallyCalculatesMarginToCenter() : void + { + $terminal = self::createMock(Terminal::class); + $terminal + ->expects($this->any()) + ->method('getWidth') + ->will($this->returnValue(200)); + + $builder = new CliMenuBuilder; + $menu = $builder + ->setTerminal($terminal) + ->setMarginAuto() + ->setWidth(100) + ->build(); + + self::assertSame(50, $menu->getStyle()->getMargin()); + } + + public function testSetMarginAutoOverwritesSetMargin() : void + { + $terminal = self::createMock(Terminal::class); + $terminal + ->expects($this->any()) + ->method('getWidth') + ->will($this->returnValue(200)); + + $builder = new CliMenuBuilder; + $menu = $builder + ->setTerminal($terminal) + ->setMargin(10) + ->setMarginAuto() + ->setWidth(100) + ->build(); + + self::assertSame(50, $menu->getStyle()->getMargin()); + } + + public function testSetMarginManuallyOverwritesSetMarginAuto() : void + { + $terminal = self::createMock(Terminal::class); + $terminal + ->expects($this->any()) + ->method('getWidth') + ->will($this->returnValue(200)); + + $builder = new CliMenuBuilder; + $menu = $builder + ->setTerminal($terminal) + ->setMarginAuto() + ->setMargin(10) + ->setWidth(100) + ->build(); + + self::assertSame(10, $menu->getStyle()->getMargin()); + } private function checkItems(CliMenu $menu, array $expected) : void { diff --git a/test/CliMenuTest.php b/test/CliMenuTest.php index eebc15e9..af288b9c 100644 --- a/test/CliMenuTest.php +++ b/test/CliMenuTest.php @@ -38,7 +38,7 @@ public function setUp() $this->terminal->expects($this->any()) ->method('getWidth') - ->willReturn(50); + ->willReturn(46); $this->terminal->expects($this->any()) ->method('write') diff --git a/test/Dialogue/ConfirmTest.php b/test/Dialogue/ConfirmTest.php index 18ce0fd5..c98168d9 100644 --- a/test/Dialogue/ConfirmTest.php +++ b/test/Dialogue/ConfirmTest.php @@ -35,7 +35,7 @@ public function setUp() $this->terminal->expects($this->any()) ->method('getWidth') - ->willReturn(50); + ->willReturn(46); $this->terminal->expects($this->any()) ->method('write') diff --git a/test/Dialogue/FlashTest.php b/test/Dialogue/FlashTest.php index 51b8c852..58ec514b 100644 --- a/test/Dialogue/FlashTest.php +++ b/test/Dialogue/FlashTest.php @@ -35,7 +35,7 @@ public function setUp() $this->terminal->expects($this->any()) ->method('getWidth') - ->willReturn(50); + ->willReturn(46); $this->terminal->expects($this->any()) ->method('write') diff --git a/test/MenuStyleTest.php b/test/MenuStyleTest.php index 37651c03..407445b4 100644 --- a/test/MenuStyleTest.php +++ b/test/MenuStyleTest.php @@ -160,7 +160,7 @@ public function testWidthCalculation() : void $style->setPadding(5); $style->setMargin(5); - static::assertSame(280, $style->getContentWidth()); + static::assertSame(290, $style->getContentWidth()); } public function testRightHandPaddingCalculation() : void @@ -171,6 +171,46 @@ public function testRightHandPaddingCalculation() : void $style->setPadding(5); $style->setMargin(5); - static::assertSame(235, $style->getRightHandPadding(50)); + static::assertSame(245, $style->getRightHandPadding(50)); + } + + public function testMargin() : void + { + $style = $this->getMenuStyle(); + + $style->setWidth(300); + $style->setPadding(5); + $style->setMargin(5); + + self::assertSame(5, $style->getMargin()); + } + + public function testMarginAutoCenters() : void + { + $style = $this->getMenuStyle(); + + $style->setWidth(300); + $style->setPadding(5); + $style->setMarginAuto(); + + self::assertSame(100, $style->getMargin()); + self::assertSame(290, $style->getContentWidth()); + } + + public function testModifyWithWhenMarginAutoIsEnabledRecalculatesMargin() : void + { + $style = $this->getMenuStyle(); + + $style->setWidth(300); + $style->setPadding(5); + $style->setMarginAuto(); + + self::assertSame(100, $style->getMargin()); + self::assertSame(290, $style->getContentWidth()); + + $style->setWidth(400); + + self::assertSame(50, $style->getMargin()); + self::assertSame(390, $style->getContentWidth()); } }