diff --git a/src/CliMenu.php b/src/CliMenu.php index 1ea45664..43d6da09 100644 --- a/src/CliMenu.php +++ b/src/CliMenu.php @@ -157,7 +157,7 @@ public function isOpen() : bool public function addItem(MenuItemInterface $item) : void { $this->items[] = $item; - + if (count($this->items) === 1) { $this->selectFirstItem(); } @@ -318,7 +318,7 @@ public function redraw(bool $clear = false) : void if ($clear) { $this->terminal->clear(); } - + $this->assertOpen(); $this->draw(); } @@ -343,8 +343,11 @@ protected function draw() : void $frame->addRows($this->style->getBorderTopRows()); } + if ($this->style->getPaddingTopBottom() > 0) { + $frame->addRows($this->style->getPaddingTopBottomRows()); + } + if ($this->title) { - $frame->addRows($this->drawMenuItem(new LineBreakItem())); $frame->addRows($this->drawMenuItem(new StaticItem($this->title))); $frame->addRows($this->drawMenuItem(new LineBreakItem($this->style->getTitleSeparator()))); } @@ -353,14 +356,17 @@ protected function draw() : void $frame->addRows($this->drawMenuItem($item, $index === $this->selectedItem)); }, $this->items, array_keys($this->items)); - $frame->addRows($this->drawMenuItem(new LineBreakItem())); - + + if ($this->style->getPaddingTopBottom() > 0) { + $frame->addRows($this->style->getPaddingTopBottomRows()); + } + if ($this->style->getBorderBottomWidth() > 0) { $frame->addRows($this->style->getBorderBottomRows()); } $frame->newLine(2); - + $this->terminal->moveCursorToTop(); foreach ($frame->getRows() as $row) { if ($row == "\n") { @@ -401,7 +407,7 @@ protected function drawMenuItem(MenuItemInterface $item, bool $selected = false) str_repeat(' ', $this->style->getBorderLeftWidth()), $this->style->getColoursSetCode(), $invertedColoursSetCode, - str_repeat(' ', $this->style->getPadding()), + str_repeat(' ', $this->style->getPaddingLeftRight()), $row, str_repeat(' ', $this->style->getRightHandPadding(mb_strlen(s::stripAnsiEscapeSequence($row)))), $invertedColoursUnsetCode, @@ -439,7 +445,7 @@ public function close() : void $menu->closeThis(); $menu = $menu->getParent(); } while (null !== $menu); - + $this->tearDownTerminal(); } diff --git a/src/CliMenuBuilder.php b/src/CliMenuBuilder.php index 8c46e057..62d092ca 100644 --- a/src/CliMenuBuilder.php +++ b/src/CliMenuBuilder.php @@ -12,7 +12,6 @@ use PhpSchool\CliMenu\MenuItem\StaticItem; use PhpSchool\CliMenu\Terminal\TerminalFactory; use PhpSchool\CliMenu\Util\ColourUtil; -use Assert\Assertion; use PhpSchool\Terminal\Terminal; use RuntimeException; @@ -31,7 +30,7 @@ class CliMenuBuilder * @var null|self */ private $parent; - + /** * @var self[] */ @@ -46,7 +45,7 @@ class CliMenuBuilder * @var string */ private $goBackButtonText = 'Go Back'; - + /** * @var string */ @@ -121,7 +120,7 @@ public function addItems(array $items) : self foreach ($items as $item) { $this->addItem(...$item); } - + return $this; } @@ -152,12 +151,12 @@ public function addAsciiArt(string $art, string $position = AsciiArtItem::POSITI public function addSubMenu(string $id, CliMenuBuilder $subMenuBuilder = null) : CliMenuBuilder { $this->menuItems[] = $id; - + if (null === $subMenuBuilder) { $this->subMenuBuilders[$id] = new static($this); return $this->subMenuBuilders[$id]; } - + $this->subMenuBuilders[$id] = $subMenuBuilder; return $this; } @@ -188,14 +187,14 @@ public function isMenuDisabled() : bool public function setGoBackButtonText(string $goBackButtonTest) : self { $this->goBackButtonText = $goBackButtonTest; - + return $this; } public function setExitButtonText(string $exitButtonText) : self { $this->exitButtonText = $exitButtonText; - + return $this; } @@ -228,9 +227,28 @@ public function setWidth(int $width) : self return $this; } - public function setPadding(int $padding) : self + public function setPadding(int $topBottom, int $leftRight = null) : self { - $this->style['padding'] = $padding; + if ($leftRight === null) { + $leftRight = $topBottom; + } + + $this->setPaddingTopBottom($topBottom); + $this->setPaddingLeftRight($leftRight); + + return $this; + } + + public function setPaddingTopBottom(int $topBottom) : self + { + $this->style['paddingTopBottom'] = $topBottom; + + return $this; + } + + public function setPaddingLeftRight(int $leftRight) : self + { + $this->style['paddingLeftRight'] = $leftRight; return $this; } @@ -238,14 +256,12 @@ public function setPadding(int $padding) : self 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; @@ -330,7 +346,7 @@ private function getDefaultItems() : array if ($this->parent) { $actions[] = new SelectableItem($this->goBackButtonText, new GoBackAction); } - + $actions[] = new SelectableItem($this->exitButtonText, new ExitAction); return $actions; } @@ -372,7 +388,8 @@ private function buildStyle() : MenuStyle ->setFg($this->style['fg']) ->setBg($this->style['bg']) ->setWidth($this->style['width']) - ->setPadding($this->style['padding']) + ->setPaddingTopBottom($this->style['paddingTopBottom']) + ->setPaddingLeftRight($this->style['paddingLeftRight']) ->setSelectedMarker($this->style['selectedMarker']) ->setUnselectedMarker($this->style['unselectedMarker']) ->setItemExtra($this->style['itemExtra']) @@ -385,7 +402,7 @@ private function buildStyle() : MenuStyle ->setBorderColour($this->style['borderColour']); $this->style['marginAuto'] ? $style->setMarginAuto() : $style->setMargin($this->style['margin']); - + return $style; } @@ -447,7 +464,7 @@ public function build() : CliMenu $this->terminal, $this->getMenuStyle() ); - + foreach ($this->subMenus as $subMenu) { $subMenu->setParent($menu); } diff --git a/src/Dialogue/Confirm.php b/src/Dialogue/Confirm.php index 98ffb2fb..65dd6db7 100644 --- a/src/Dialogue/Confirm.php +++ b/src/Dialogue/Confirm.php @@ -27,9 +27,9 @@ public function display(string $confirmText = 'OK') : void $this->write(sprintf( "%s%s%s%s%s\n", $this->style->getColoursSetCode(), - str_repeat(' ', $this->style->getPadding()), + str_repeat(' ', $this->style->getPaddingLeftRight()), $this->text, - str_repeat(' ', $this->style->getPadding()), + str_repeat(' ', $this->style->getPaddingLeftRight()), $this->style->getColoursResetCode() )); @@ -52,9 +52,9 @@ public function display(string $confirmText = 'OK') : void $this->write(sprintf( "%s%s%s%s%s\n", $this->style->getColoursSetCode(), - str_repeat(' ', $this->style->getPadding()), + str_repeat(' ', $this->style->getPaddingLeftRight()), str_repeat(' ', mb_strlen($this->text)), - str_repeat(' ', $this->style->getPadding()), + str_repeat(' ', $this->style->getPaddingLeftRight()), $this->style->getColoursResetCode() )); diff --git a/src/Dialogue/Dialogue.php b/src/Dialogue/Dialogue.php index 91ec4de2..6550037c 100644 --- a/src/Dialogue/Dialogue.php +++ b/src/Dialogue/Dialogue.php @@ -73,7 +73,7 @@ protected function calculateCoordinates() : void //x $parentStyle = $this->parentMenu->getStyle(); - $dialogueHalfLength = (mb_strlen($this->text) + ($this->style->getPadding() * 2)) / 2; + $dialogueHalfLength = (mb_strlen($this->text) + ($this->style->getPaddingLeftRight() * 2)) / 2; $widthHalfLength = ceil($parentStyle->getWidth() / 2 + $parentStyle->getMargin()); $this->x = $widthHalfLength - $dialogueHalfLength; } @@ -87,9 +87,9 @@ protected function emptyRow() : void sprintf( "%s%s%s%s%s\n", $this->style->getColoursSetCode(), - str_repeat(' ', $this->style->getPadding()), + str_repeat(' ', $this->style->getPaddingLeftRight()), str_repeat(' ', mb_strlen($this->text)), - str_repeat(' ', $this->style->getPadding()), + str_repeat(' ', $this->style->getPaddingLeftRight()), $this->style->getColoursResetCode() ) ); diff --git a/src/Dialogue/Flash.php b/src/Dialogue/Flash.php index b38ef0ba..deba12df 100644 --- a/src/Dialogue/Flash.php +++ b/src/Dialogue/Flash.php @@ -24,9 +24,9 @@ public function display() : void $this->write(sprintf( "%s%s%s%s%s\n", $this->style->getColoursSetCode(), - str_repeat(' ', $this->style->getPadding()), + str_repeat(' ', $this->style->getPaddingLeftRight()), $this->text, - str_repeat(' ', $this->style->getPadding()), + str_repeat(' ', $this->style->getPaddingLeftRight()), $this->style->getColoursResetCode() )); diff --git a/src/Input/InputIO.php b/src/Input/InputIO.php index 739a8174..663f8488 100644 --- a/src/Input/InputIO.php +++ b/src/Input/InputIO.php @@ -131,7 +131,7 @@ private function calculateXPosition(Input $input, string $userInput) : int ); $parentStyle = $this->parentMenu->getStyle(); - $halfWidth = ($width + ($input->getStyle()->getPadding() * 2)) / 2; + $halfWidth = ($width + ($input->getStyle()->getPaddingLeftRight() * 2)) / 2; $parentHalfWidth = ceil($parentStyle->getWidth() / 2 + $parentStyle->getMargin()); return $parentHalfWidth - $halfWidth; @@ -144,9 +144,9 @@ private function drawLine(Input $input, string $userInput, string $text) : void $line = sprintf( "%s%s%s%s%s\n", $input->getStyle()->getColoursSetCode(), - str_repeat(' ', $input->getStyle()->getPadding()), + str_repeat(' ', $input->getStyle()->getPaddingLeftRight()), $text, - str_repeat(' ', $input->getStyle()->getPadding()), + str_repeat(' ', $input->getStyle()->getPaddingLeftRight()), $input->getStyle()->getColoursResetCode() ); diff --git a/src/MenuItem/AsciiArtItem.php b/src/MenuItem/AsciiArtItem.php index d1a39024..047bc37c 100644 --- a/src/MenuItem/AsciiArtItem.php +++ b/src/MenuItem/AsciiArtItem.php @@ -16,7 +16,7 @@ class AsciiArtItem implements MenuItemInterface const POSITION_CENTER = 'center'; const POSITION_LEFT = 'left'; const POSITION_RIGHT = 'right'; - + /** * @var string */ @@ -40,7 +40,7 @@ class AsciiArtItem implements MenuItemInterface public function __construct(string $text, string $position = self::POSITION_CENTER, string $alt = '') { Assertion::inArray($position, [self::POSITION_CENTER, self::POSITION_RIGHT, self::POSITION_LEFT]); - + $this->text = implode("\n", array_map(function (string $line) { return rtrim($line, ' '); }, explode("\n", $text))); @@ -60,7 +60,7 @@ public function getRows(MenuStyle $style, bool $selected = false) : array } $padding = $style->getContentWidth() - $this->artLength; - + return array_map(function ($row) use ($padding) { switch ($this->position) { case self::POSITION_LEFT: diff --git a/src/MenuStyle.php b/src/MenuStyle.php index 42b1bf4d..44477148 100644 --- a/src/MenuStyle.php +++ b/src/MenuStyle.php @@ -6,6 +6,7 @@ use PhpSchool\CliMenu\Terminal\TerminalFactory; use PhpSchool\CliMenu\Util\ColourUtil; use PhpSchool\Terminal\Terminal; +use Assert\Assertion; //TODO: B/W fallback @@ -37,12 +38,22 @@ class MenuStyle /** * @var int */ - protected $padding; + protected $margin; /** * @var int */ - protected $margin; + protected $paddingTopBottom; + + /** + * @var int + */ + protected $paddingLeftRight; + + /** + * @var array + */ + private $paddingTopBottomRows = []; /** * @var int @@ -143,7 +154,8 @@ class MenuStyle 'fg' => 'white', 'bg' => 'blue', 'width' => 100, - 'padding' => 2, + 'paddingTopBottom' => 1, + 'paddingLeftRight' => 2, 'margin' => 2, 'selectedMarker' => '●', 'unselectedMarker' => '○', @@ -214,11 +226,12 @@ public function __construct(Terminal $terminal = null) $this->fg = static::$defaultStyleValues['fg']; $this->bg = static::$defaultStyleValues['bg']; - + $this->generateColoursSetCode(); - + $this->setWidth(static::$defaultStyleValues['width']); - $this->setPadding(static::$defaultStyleValues['padding']); + $this->setPaddingTopBottom(static::$defaultStyleValues['paddingTopBottom']); + $this->setPaddingLeftRight(static::$defaultStyleValues['paddingLeftRight']); $this->setMargin(static::$defaultStyleValues['margin']); $this->setSelectedMarker(static::$defaultStyleValues['selectedMarker']); $this->setUnselectedMarker(static::$defaultStyleValues['unselectedMarker']); @@ -300,8 +313,12 @@ public function getColoursResetCode() : string protected function calculateContentWidth() : void { $this->contentWidth = $this->width - - ($this->padding * 2) + - ($this->paddingLeftRight * 2) - ($this->borderRightWidth + $this->borderLeftWidth); + + if ($this->contentWidth < 0) { + $this->contentWidth = 0; + } } public function getFg() @@ -333,7 +350,9 @@ public function setBg(string $bg, string $fallback = null) : self $bg, $fallback ); + $this->generateColoursSetCode(); + $this->generatePaddingTopBottomRows(); return $this; } @@ -345,6 +364,8 @@ public function getWidth() : int public function setWidth(int $width) : self { + Assertion::greaterOrEqualThan($width, 0); + if ($width >= $this->terminal->getWidth()) { $width = $this->terminal->getWidth(); } @@ -356,20 +377,83 @@ public function setWidth(int $width) : self $this->calculateContentWidth(); $this->generateBorderRows(); + $this->generatePaddingTopBottomRows(); + + return $this; + } + + public function getPaddingTopBottom() : int + { + return $this->paddingTopBottom; + } + + public function getPaddingLeftRight() : int + { + return $this->paddingLeftRight; + } + + private function generatePaddingTopBottomRows() : void + { + if ($this->borderLeftWidth || $this->borderRightWidth) { + $borderColour = $this->getBorderColourCode(); + } else { + $borderColour = ''; + } + + $paddingRow = sprintf( + "%s%s%s%s%s%s%s%s%s%s\n", + str_repeat(' ', $this->margin), + $borderColour, + str_repeat(' ', $this->borderRightWidth), + $this->getColoursSetCode(), + str_repeat(' ', $this->paddingLeftRight), + str_repeat(' ', $this->contentWidth), + str_repeat(' ', $this->paddingLeftRight), + $borderColour, + str_repeat(' ', $this->borderRightWidth), + $this->coloursResetCode + ); + + $this->paddingTopBottomRows = array_fill(0, $this->paddingTopBottom, $paddingRow); + } + + public function getPaddingTopBottomRows() : array + { + return $this->paddingTopBottomRows; + } + + public function setPadding(int $topBottom, int $leftRight = null) : self + { + if ($leftRight === null) { + $leftRight = $topBottom; + } + + $this->setPaddingTopBottom($topBottom); + $this->setPaddingLeftRight($leftRight); + + $this->calculateContentWidth(); + $this->generatePaddingTopBottomRows(); return $this; } - public function getPadding() : int + public function setPaddingTopBottom(int $topBottom) : self { - return $this->padding; + Assertion::greaterOrEqualThan($topBottom, 0); + $this->paddingTopBottom = $topBottom; + + $this->generatePaddingTopBottomRows(); + + return $this; } - public function setPadding(int $padding) : self + public function setPaddingLeftRight(int $leftRight) : self { - $this->padding = $padding; + Assertion::greaterOrEqualThan($leftRight, 0); + $this->paddingLeftRight = $leftRight; $this->calculateContentWidth(); + $this->generatePaddingTopBottomRows(); return $this; } @@ -385,16 +469,20 @@ public function setMarginAuto() : self $this->margin = floor(($this->terminal->getWidth() - $this->width) / 2); $this->generateBorderRows(); + $this->generatePaddingTopBottomRows(); return $this; } public function setMargin(int $margin) : self { + Assertion::greaterOrEqualThan($margin, 0); + $this->marginAuto = false; $this->margin = $margin; $this->generateBorderRows(); + $this->generatePaddingTopBottomRows(); return $this; } @@ -409,7 +497,7 @@ public function getContentWidth() : int */ public function getRightHandPadding(int $contentLength) : int { - $rightPadding = $this->getContentWidth() - $contentLength + $this->getPadding(); + $rightPadding = $this->getContentWidth() - $contentLength + $this->getPaddingLeftRight(); if ($rightPadding < 0) { $rightPadding = 0; @@ -545,6 +633,7 @@ public function setBorder( $this->calculateContentWidth(); $this->generateBorderRows(); + $this->generatePaddingTopBottomRows(); return $this; } @@ -563,6 +652,8 @@ public function setBorderRightWidth(int $width) : self $this->borderRightWidth = $width; $this->calculateContentWidth(); + $this->generatePaddingTopBottomRows(); + return $this; } @@ -580,6 +671,8 @@ public function setBorderLeftWidth(int $width) : self $this->borderLeftWidth = $width; $this->calculateContentWidth(); + $this->generatePaddingTopBottomRows(); + return $this; } @@ -592,6 +685,7 @@ public function setBorderColour(string $colour, $fallback = null) : self ); $this->generateBorderRows(); + $this->generatePaddingTopBottomRows(); return $this; } diff --git a/test/CliMenuBuilderTest.php b/test/CliMenuBuilderTest.php index 8f6f1227..568b8e06 100644 --- a/test/CliMenuBuilderTest.php +++ b/test/CliMenuBuilderTest.php @@ -55,7 +55,7 @@ public function testModifyStyles() : void $builder->setBackgroundColour('red'); $builder->setForegroundColour('red'); $builder->setWidth(40); - $builder->setPadding(4); + $builder->setPadding(4, 1); $builder->setMargin(4); $builder->setUnselectedMarker('>'); $builder->setSelectedMarker('x'); @@ -75,7 +75,8 @@ public function testModifyStyles() : void $this->checkStyleVariable($menu, 'bg', 'red'); $this->checkStyleVariable($menu, 'fg', 'red'); $this->checkStyleVariable($menu, 'width', 40); - $this->checkStyleVariable($menu, 'padding', 4); + $this->checkStyleVariable($menu, 'paddingTopBottom', 4); + $this->checkStyleVariable($menu, 'paddingLeftRight', 1); $this->checkStyleVariable($menu, 'margin', 4); $this->checkStyleVariable($menu, 'unselectedMarker', '>'); $this->checkStyleVariable($menu, 'selectedMarker', 'x'); diff --git a/test/MenuStyleTest.php b/test/MenuStyleTest.php index 38938dc6..8602e766 100644 --- a/test/MenuStyleTest.php +++ b/test/MenuStyleTest.php @@ -34,7 +34,7 @@ private function getMenuStyle(int $colours = 8) : MenuStyle // Force recalculate terminal widths now terminal is set $style->setWidth(100); - + return $style; } @@ -107,7 +107,8 @@ public function testGetterAndSetters() : void self::assertSame('=', $style->getTitleSeparator()); self::assertSame(100, $style->getWidth()); self::assertSame(2, $style->getMargin()); - self::assertSame(2, $style->getPadding()); + self::assertSame(1, $style->getPaddingTopBottom()); + self::assertSame(2, $style->getPaddingLeftRight()); self::assertSame(0, $style->getBorderTopWidth()); self::assertSame(0, $style->getBorderRightWidth()); self::assertSame(0, $style->getBorderBottomWidth()); @@ -123,7 +124,8 @@ public function testGetterAndSetters() : void $style->setTitleSeparator('+'); $style->setWidth(200); $style->setMargin(10); - $style->setPadding(10); + $style->setPaddingTopBottom(5); + $style->setPaddingLeftRight(10); $style->setBorderTopWidth(1); $style->setBorderRightWidth(2); $style->setBorderBottomWidth(3); @@ -139,14 +141,15 @@ public function testGetterAndSetters() : void self::assertSame('+', $style->getTitleSeparator()); self::assertSame(200, $style->getWidth()); self::assertSame(10, $style->getMargin()); - self::assertSame(10, $style->getPadding()); + self::assertSame(5, $style->getPaddingTopBottom()); + self::assertSame(10, $style->getPaddingLeftRight()); self::assertSame(1, $style->getBorderTopWidth()); self::assertSame(2, $style->getBorderRightWidth()); self::assertSame(3, $style->getBorderBottomWidth()); self::assertSame(4, $style->getBorderLeftWidth()); self::assertSame('green', $style->getBorderColour()); } - + public function testSetBorderShorthandFunction() : void { $style = $this->getMenuStyle(); @@ -222,7 +225,7 @@ public function test256ColoursCodes() : void static::assertSame('16', $style->getBg()); static::assertSame('206', $style->getFg()); static::assertSame("\033[38;5;206;48;5;16m", $style->getColoursSetCode()); - + $style = $this->getMenuStyle(8); $style->setBg(16, 'white'); $style->setFg(206, 'red'); @@ -309,7 +312,7 @@ public function testRightHandPaddingReturnsZeroWhenContentLengthTooLong() : void $style->setBorder(0); $style->setWidth(100); - + self::assertEquals(0, $style->getRightHandPadding(100)); self::assertEquals(0, $style->getRightHandPadding(150)); } @@ -340,7 +343,7 @@ public function testMargin() : void self::assertSame(5, $style->getMargin()); } - + public function testMarginAutoCenters() : void { $style = $this->getMenuStyle(); @@ -363,7 +366,7 @@ public function testModifyWithWhenMarginAutoIsEnabledRecalculatesMargin() : void self::assertSame(100, $style->getMargin()); self::assertSame(290, $style->getContentWidth()); - + $style->setWidth(400); self::assertSame(50, $style->getMargin());