From 355b0506bbd3969bbdcc7c902b4f2a561e816cd9 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Thu, 27 Oct 2016 12:50:25 +0100 Subject: [PATCH 1/5] Dialogue feature --- examples/confirm.php | 24 +++ examples/flash.php | 24 +++ src/CliMenu.php | 81 +++++-- src/Dialogue/Confirm.php | 81 +++++++ src/Dialogue/Dialogue.php | 115 ++++++++++ src/Dialogue/Flash.php | 34 +++ src/Frame.php | 61 ++++++ src/Terminal/TerminalInterface.php | 7 + src/Terminal/UnixTerminal.php | 5 + test/Dialogue/ConfirmTest.php | 204 ++++++++++++++++++ test/Dialogue/FlashTest.php | 148 +++++++++++++ test/FrameTest.php | 70 ++++++ .../testConfirmCanOnlyBeClosedWithEnter.txt | 23 ++ ...tConfirmWithEvenLengthConfirmAndButton.txt | 23 ++ ...ithEvenLengthConfirmAndOddLengthButton.txt | 23 ++ ...stConfirmWithOddLengthConfirmAndButton.txt | 23 ++ ...ithOddLengthConfirmAndEvenLengthButton.txt | 23 ++ test/res/testFlashCanBeClosedWithAnyKey.txt | 21 ++ test/res/testFlashWithEvenLength.txt | 21 ++ test/res/testFlashWithOddLength.txt | 21 ++ 20 files changed, 1018 insertions(+), 14 deletions(-) create mode 100644 examples/confirm.php create mode 100644 examples/flash.php create mode 100644 src/Dialogue/Confirm.php create mode 100644 src/Dialogue/Dialogue.php create mode 100644 src/Dialogue/Flash.php create mode 100644 src/Frame.php create mode 100644 test/Dialogue/ConfirmTest.php create mode 100644 test/Dialogue/FlashTest.php create mode 100644 test/FrameTest.php create mode 100644 test/res/testConfirmCanOnlyBeClosedWithEnter.txt create mode 100644 test/res/testConfirmWithEvenLengthConfirmAndButton.txt create mode 100644 test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt create mode 100644 test/res/testConfirmWithOddLengthConfirmAndButton.txt create mode 100644 test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt create mode 100644 test/res/testFlashCanBeClosedWithAnyKey.txt create mode 100644 test/res/testFlashWithEvenLength.txt create mode 100644 test/res/testFlashWithOddLength.txt diff --git a/examples/confirm.php b/examples/confirm.php new file mode 100644 index 00000000..2d34b447 --- /dev/null +++ b/examples/confirm.php @@ -0,0 +1,24 @@ +confirm('PHP School FTW!') + ->display('OK'); +}; + +$menu = (new CliMenuBuilder) + ->setTitle('Basic CLI Menu') + ->addItem('First Item', $itemCallable) + ->addItem('Second Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addLineBreak('-') + ->build(); + +$menu->open(); diff --git a/examples/flash.php b/examples/flash.php new file mode 100644 index 00000000..8d362743 --- /dev/null +++ b/examples/flash.php @@ -0,0 +1,24 @@ +flash("PHP School FTW!!") + ->display(); +}; + +$menu = (new CliMenuBuilder) + ->setTitle('Basic CLI Menu') + ->addItem('First Item', $itemCallable) + ->addItem('Second Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addLineBreak('-') + ->build(); + +$menu->open(); diff --git a/src/CliMenu.php b/src/CliMenu.php index fce79909..2bd5e4cb 100644 --- a/src/CliMenu.php +++ b/src/CliMenu.php @@ -8,6 +8,8 @@ use PhpSchool\CliMenu\MenuItem\LineBreakItem; use PhpSchool\CliMenu\MenuItem\MenuItemInterface; use PhpSchool\CliMenu\MenuItem\StaticItem; +use PhpSchool\CliMenu\Dialogue\Confirm; +use PhpSchool\CliMenu\Dialogue\Flash; use PhpSchool\CliMenu\Terminal\TerminalFactory; use PhpSchool\CliMenu\Terminal\TerminalInterface; use PhpSchool\CliMenu\Util\StringUtil as s; @@ -55,6 +57,11 @@ class CliMenu */ protected $parent; + /** + * @var Frame|null + */ + private $currentFrame; + /** * @param string $title * @param array $items @@ -258,21 +265,29 @@ protected function draw() $this->terminal->clean(); $this->terminal->moveCursorToTop(); - echo "\n\n"; + $frame = new Frame; + + $frame->newLine(2); if (is_string($this->title)) { - $this->drawMenuItem(new LineBreakItem()); - $this->drawMenuItem(new StaticItem($this->title)); - $this->drawMenuItem(new LineBreakItem($this->style->getTitleSeparator())); + $frame->addRows($this->drawMenuItem(new LineBreakItem())); + $frame->addRows($this->drawMenuItem(new StaticItem($this->title))); + $frame->addRows($this->drawMenuItem(new LineBreakItem($this->style->getTitleSeparator()))); } - array_map(function ($item, $index) { - $this->drawMenuItem($item, $index === $this->selectedItem); + array_map(function ($item, $index) use ($frame) { + $frame->addRows($this->drawMenuItem($item, $index === $this->selectedItem)); }, $this->items, array_keys($this->items)); - $this->drawMenuItem(new LineBreakItem()); + $frame->addRows($this->drawMenuItem(new LineBreakItem())); + + $frame->newLine(2); + + foreach ($frame->getRows() as $row) { + echo $row; + } - echo "\n\n"; + $this->currentFrame = $frame; } /** @@ -280,6 +295,7 @@ protected function draw() * * @param MenuItemInterface $item * @param bool|false $selected + * @return array */ protected function drawMenuItem(MenuItemInterface $item, $selected = false) { @@ -293,9 +309,9 @@ protected function drawMenuItem(MenuItemInterface $item, $selected = false) ? $this->style->getSelectedUnsetCode() : $this->style->getUnselectedUnsetCode(); - foreach ($rows as $row) { - echo sprintf( - "%s%s%s%s%s%s%s", + return array_map(function ($row) use ($setColour, $unsetColour) { + return sprintf( + "%s%s%s%s%s%s%s\n\r", str_repeat(' ', $this->style->getMargin()), $setColour, str_repeat(' ', $this->style->getPadding()), @@ -304,9 +320,7 @@ protected function drawMenuItem(MenuItemInterface $item, $selected = false) $unsetColour, str_repeat(' ', $this->style->getMargin()) ); - - echo "\n\r"; - } + }, $rows); } /** @@ -379,4 +393,43 @@ public function getStyle() { return $this->style; } + + public function getCurrentFrame() + { + return $this->currentFrame; + } + + /** + * @param string $text + * @return Flash + */ + public function flash($text) + { + if (strpos($text, "\n") !== false) { + throw new \InvalidArgumentException; + } + + $style = (new MenuStyle($this->terminal)) + ->setBg('yellow') + ->setFg('red'); + + return new Flash($this, $style, $this->terminal, $text); + } + + /** + * @param string $text + * @return Confirm + */ + public function confirm($text) + { + if (strpos($text, "\n") !== false) { + throw new \InvalidArgumentException; + } + + $style = (new MenuStyle($this->terminal)) + ->setBg('yellow') + ->setFg('red'); + + return new Confirm($this, $style, $this->terminal, $text); + } } diff --git a/src/Dialogue/Confirm.php b/src/Dialogue/Confirm.php new file mode 100644 index 00000000..84003c1a --- /dev/null +++ b/src/Dialogue/Confirm.php @@ -0,0 +1,81 @@ + + */ +class Confirm extends Dialogue +{ + + /** + * Display confirmation with a button with the given text + * + * @param string $confirmText + */ + public function display($confirmText = 'OK') + { + $this->assertMenuOpen(); + + $this->terminal->moveCursorToRow($this->y); + + $promptWidth = mb_strlen($this->text) + 4; + + $this->emptyRow(); + + $this->write(sprintf( + "%s %s %s\n", + $this->style->getUnselectedSetCode(), + $this->text, + $this->style->getUnselectedUnsetCode() + )); + + $this->emptyRow(); + + $confirmText = sprintf(' < %s > ', $confirmText); + $leftFill = ($promptWidth / 2) - (mb_strlen($confirmText) / 2); + + $this->write(sprintf( + '%s%s%s', + $this->style->getUnselectedSetCode(), + str_repeat(' ', $leftFill), + $this->style->getUnselectedSetCode() + )); + + $this->write( + sprintf( + '%s%s%s', + $this->style->getSelectedSetCode(), + $confirmText, + $this->style->getSelectedUnsetCode() + ), + -1 + ); + + $this->write( + sprintf( + "%s%s%s\n", + $this->style->getUnselectedSetCode(), + str_repeat(' ', ceil($promptWidth - $leftFill - mb_strlen($confirmText))), + $this->style->getSelectedUnsetCode() + ), + -1 + ); + + $this->write(sprintf( + "%s %s %s\n", + $this->style->getUnselectedSetCode(), + str_repeat(' ', mb_strlen($this->text)), + $this->style->getUnselectedUnsetCode() + )); + + $this->terminal->moveCursorToTop(); + $input = $this->terminal->getKeyedInput(); + + while ($input !== 'enter') { + $input = $this->terminal->getKeyedInput(); + } + + $this->parentMenu->redraw(); + } +} diff --git a/src/Dialogue/Dialogue.php b/src/Dialogue/Dialogue.php new file mode 100644 index 00000000..93ea4464 --- /dev/null +++ b/src/Dialogue/Dialogue.php @@ -0,0 +1,115 @@ + + */ +abstract class Dialogue +{ + /** + * @var MenuStyle + */ + protected $style; + + /** + * @var CliMenu + */ + protected $parentMenu; + + /** + * @var TerminalInterface + */ + protected $terminal; + + /** + * @var string $text + */ + protected $text; + + /** + * @var int + */ + protected $x; + + /** + * @var int + */ + protected $y; + + /** + * @param CliMenu $parentMenu + * @param MenuStyle $menuStyle + * @param TerminalInterface $terminal + * @param string $text + */ + public function __construct(CliMenu $parentMenu, MenuStyle $menuStyle, TerminalInterface $terminal, $text) + { + $this->style = $menuStyle; + $this->terminal = $terminal; + $this->text = $text; + $this->parentMenu = $parentMenu; + + $this->calculateCoordinates(); + } + + /** + * @throws MenuNotOpenException + */ + protected function assertMenuOpen() + { + if (!$this->parentMenu->isOpen()) { + throw new MenuNotOpenException; + } + } + + /** + * Calculate the co-ordinates to write the messages + */ + protected function calculateCoordinates() + { + $textLines = count(explode("\n", $this->text)) + 2; + $this->y = ceil(($this->parentMenu->getCurrentFrame()->count() / 2)) - ceil($textLines / 2) + 1; + $this->x = ($this->style->getWidth() / 2) - (mb_strlen($this->text) / 2); + } + + /** + * Write an empty row + */ + protected function emptyRow() + { + $this->write( + sprintf( + "%s %s %s\n", + $this->style->getUnselectedSetCode(), + str_repeat(' ', mb_strlen($this->text)), + $this->style->getUnselectedUnsetCode() + ) + ); + } + + /** + * Write some text at a particular column + * + * @param int $column + * @param string $text + */ + protected function write($text, $column = null) + { + $this->terminal->moveCursorToColumn($column ?: $this->x); + echo $text; + } + + /** + * @return MenuStyle + */ + public function getStyle() + { + return $this->style; + } +} diff --git a/src/Dialogue/Flash.php b/src/Dialogue/Flash.php new file mode 100644 index 00000000..25025b46 --- /dev/null +++ b/src/Dialogue/Flash.php @@ -0,0 +1,34 @@ + + */ +class Flash extends Dialogue +{ + /** + * Flash a message on top of the menu which + * disappears on any keystroke. + */ + public function display() + { + $this->assertMenuOpen(); + + $this->terminal->moveCursorToRow($this->y); + + $this->emptyRow(); + + $this->write(sprintf( + "%s %s %s\n", + $this->style->getUnselectedSetCode(), + $this->text, + $this->style->getUnselectedUnsetCode() + )); + + $this->emptyRow(); + $this->terminal->moveCursorToTop(); + $this->terminal->getKeyedInput(); + $this->parentMenu->redraw(); + } +} diff --git a/src/Frame.php b/src/Frame.php new file mode 100644 index 00000000..93a571bd --- /dev/null +++ b/src/Frame.php @@ -0,0 +1,61 @@ + + */ +class Frame implements \Countable +{ + /** + * @var array + */ + private $rows = []; + + /** + * @param int $count + */ + public function newLine($count = 1) + { + foreach (range(1, $count) as $i) { + $this->rows[] = "\n"; + } + } + + /** + * @param array $rows + */ + public function addRows(array $rows = []) + { + foreach ($rows as $row) { + $this->rows[] = $row; + } + } + + /** + * @param $row + */ + public function addRow($row) + { + $this->rows[] = $row; + } + + /** + * @return int + */ + public function count() + { + return count($this->rows); + } + + /** + * @return array + */ + public function getRows() + { + return $this->rows; + } +} diff --git a/src/Terminal/TerminalInterface.php b/src/Terminal/TerminalInterface.php index 77634405..ead962e8 100644 --- a/src/Terminal/TerminalInterface.php +++ b/src/Terminal/TerminalInterface.php @@ -87,6 +87,13 @@ public function moveCursorToTop(); */ public function moveCursorToRow($rowNumber); + /** + * Move the cursor to a specific column + * + * @param int $columnNumber + */ + public function moveCursorToColumn($columnNumber); + /** * Clean the whole console without jumping the window * diff --git a/src/Terminal/UnixTerminal.php b/src/Terminal/UnixTerminal.php index 6e7d44b9..a395db31 100644 --- a/src/Terminal/UnixTerminal.php +++ b/src/Terminal/UnixTerminal.php @@ -217,6 +217,11 @@ public function moveCursorToRow($rowNumber) echo sprintf("\033[%d;0H", $rowNumber); } + public function moveCursorToColumn($column) + { + echo sprintf("\033[%dC", $column); + } + /** * Clear the current cursors line * diff --git a/test/Dialogue/ConfirmTest.php b/test/Dialogue/ConfirmTest.php new file mode 100644 index 00000000..a0e16c20 --- /dev/null +++ b/test/Dialogue/ConfirmTest.php @@ -0,0 +1,204 @@ + + */ +class ConfirmTest extends PHPUnit_Framework_TestCase +{ + public function testConfirmWithOddLengthConfirmAndButton() + { + $terminal = $this->createMock(TerminalInterface::class); + + $terminal->expects($this->any()) + ->method('isTTY') + ->willReturn(true); + + $terminal + ->method('getKeyedInput') + ->will($this->onConsecutiveCalls( + 'enter', + 'enter' + )); + + $terminal->expects($this->any()) + ->method('getWidth') + ->willReturn(50); + + $style = $this->getStyle($terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->confirm('PHP School FTW!') + ->display('OK!'); + + $menu->close(); + }); + + $this->expectOutputString(file_get_contents($this->getTestFile())); + + $menu = new CliMenu('PHP School FTW', [$item], $terminal, $style); + $menu->open(); + } + + public function testConfirmWithEvenLengthConfirmAndButton() + { + $terminal = $this->createMock(TerminalInterface::class); + + $terminal->expects($this->any()) + ->method('isTTY') + ->willReturn(true); + + $terminal + ->method('getKeyedInput') + ->will($this->onConsecutiveCalls( + 'enter', + 'enter' + )); + + $terminal->expects($this->any()) + ->method('getWidth') + ->willReturn(50); + + $style = $this->getStyle($terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->confirm('PHP School FTW') + ->display('OK'); + + $menu->close(); + }); + + $this->expectOutputString(file_get_contents($this->getTestFile())); + + $menu = new CliMenu('PHP School FTW', [$item], $terminal, $style); + $menu->open(); + } + + public function testConfirmWithEvenLengthConfirmAndOddLengthButton() + { + $terminal = $this->createMock(TerminalInterface::class); + + $terminal->expects($this->any()) + ->method('isTTY') + ->willReturn(true); + + $terminal + ->method('getKeyedInput') + ->will($this->onConsecutiveCalls( + 'enter', + 'enter' + )); + + $terminal->expects($this->any()) + ->method('getWidth') + ->willReturn(50); + + $style = $this->getStyle($terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->confirm('PHP School FTW') + ->display('OK!'); + + $menu->close(); + }); + + $this->expectOutputString(file_get_contents($this->getTestFile())); + + $menu = new CliMenu('PHP School FTW', [$item], $terminal, $style); + $menu->open(); + } + + public function testConfirmWithOddLengthConfirmAndEvenLengthButton() + { + $terminal = $this->createMock(TerminalInterface::class); + + $terminal->expects($this->any()) + ->method('isTTY') + ->willReturn(true); + + $terminal + ->method('getKeyedInput') + ->will($this->onConsecutiveCalls( + 'enter', + 'enter' + )); + + $terminal->expects($this->any()) + ->method('getWidth') + ->willReturn(50); + + $style = $this->getStyle($terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->confirm('PHP School FTW!') + ->display('OK'); + + $menu->close(); + }); + + $this->expectOutputString(file_get_contents($this->getTestFile())); + + $menu = new CliMenu('PHP School FTW', [$item], $terminal, $style); + $menu->open(); + } + + public function testConfirmCanOnlyBeClosedWithEnter() + { + $terminal = $this->createMock(TerminalInterface::class); + + $terminal->expects($this->any()) + ->method('isTTY') + ->willReturn(true); + + $terminal + ->method('getKeyedInput') + ->will($this->onConsecutiveCalls( + 'enter', + 'up', + 'down', + 'enter' + )); + + $terminal->expects($this->any()) + ->method('getWidth') + ->willReturn(50); + + $style = $this->getStyle($terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->confirm('PHP School FTW!') + ->display('OK'); + + $menu->close(); + }); + + $this->expectOutputString(file_get_contents($this->getTestFile())); + + $menu = new CliMenu('PHP School FTW', [$item], $terminal, $style); + $menu->open(); + } + + /** + * @return string + */ + private function getTestFile() + { + return sprintf('%s/../res/%s.txt', __DIR__, $this->getName()); + } + + /** + * @param TerminalInterface $terminal + * @return MenuStyle + */ + private function getStyle(TerminalInterface $terminal) + { + return new MenuStyle($terminal); + } +} diff --git a/test/Dialogue/FlashTest.php b/test/Dialogue/FlashTest.php new file mode 100644 index 00000000..9f19c729 --- /dev/null +++ b/test/Dialogue/FlashTest.php @@ -0,0 +1,148 @@ + + */ +class FlashTest extends PHPUnit_Framework_TestCase +{ + public function testFlashWithOddLength() + { + $terminal = $this->createMock(TerminalInterface::class); + + $terminal->expects($this->any()) + ->method('isTTY') + ->willReturn(true); + + $terminal + ->method('getKeyedInput') + ->will($this->onConsecutiveCalls( + 'enter', + 'enter' + )); + + $terminal->expects($this->any()) + ->method('getWidth') + ->willReturn(50); + + $style = $this->getStyle($terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->flash('PHP School FTW!') + ->display(); + + $menu->close(); + }); + + $this->expectOutputString(file_get_contents($this->getTestFile())); + + $menu = new CliMenu('PHP School FTW', [$item], $terminal, $style); + $menu->open(); + } + + public function testFlashWithEvenLength() + { + $terminal = $this->createMock(TerminalInterface::class); + + $terminal->expects($this->any()) + ->method('isTTY') + ->willReturn(true); + + $terminal + ->method('getKeyedInput') + ->will($this->onConsecutiveCalls( + 'enter', + 'enter' + )); + + $terminal->expects($this->any()) + ->method('getWidth') + ->willReturn(50); + + $style = $this->getStyle($terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->flash('PHP School FTW') + ->display(); + + $menu->close(); + }); + + $this->expectOutputString(file_get_contents($this->getTestFile())); + + $menu = new CliMenu('PHP School FTW', [$item], $terminal, $style); + $menu->open(); + } + + /** + * @dataProvider keyProvider + * @param string $key + */ + public function testFlashCanBeClosedWithAnyKey($key) + { + $terminal = $this->createMock(TerminalInterface::class); + + $terminal->expects($this->any()) + ->method('isTTY') + ->willReturn(true); + + $terminal + ->method('getKeyedInput') + ->will($this->onConsecutiveCalls('enter', $key)); + + $terminal->expects($this->any()) + ->method('getWidth') + ->willReturn(50); + + $style = $this->getStyle($terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->flash('PHP School FTW!') + ->display(); + + $menu->close(); + }); + + $this->expectOutputString(file_get_contents($this->getTestFile())); + + $menu = new CliMenu('PHP School FTW', [$item], $terminal, $style); + $menu->open(); + } + + /** + * @return array + */ + public function keyProvider() + { + return [ + ['enter'], + ['right'], + ['down'], + ['up'], + ]; + } + + /** + * @return string + */ + private function getTestFile() + { + return sprintf('%s/../res/%s.txt', __DIR__, $this->getName(false)); + } + + /** + * @param TerminalInterface $terminal + * @return MenuStyle + */ + private function getStyle(TerminalInterface $terminal) + { + return new MenuStyle($terminal); + } +} diff --git a/test/FrameTest.php b/test/FrameTest.php new file mode 100644 index 00000000..8ab04387 --- /dev/null +++ b/test/FrameTest.php @@ -0,0 +1,70 @@ + + */ +class FrameTest extends PHPUnit_Framework_TestCase +{ + public function testNewLine() + { + $frame = new Frame; + $frame->newLine(); + + $this->assertEquals(["\n"], $frame->getRows()); + + $frame = new Frame; + $frame->newLine(1); + + $this->assertEquals(["\n"], $frame->getRows()); + + $frame = new Frame; + $frame->newLine(2); + + $this->assertEquals(["\n", "\n"], $frame->getRows()); + } + + public function testAddRows() + { + $frame = new Frame; + $frame->addRows(['one', 'two']); + $this->assertEquals(['one', 'two'], $frame->getRows()); + $frame->addRows(['three']); + $this->assertEquals(['one', 'two', 'three'], $frame->getRows()); + } + + public function testAddRow() + { + $frame = new Frame; + $frame->addRow('one'); + $this->assertEquals(['one'], $frame->getRows()); + $frame->addRow('two'); + $this->assertEquals(['one', 'two'], $frame->getRows()); + } + + public function testCount() + { + $frame = new Frame; + $frame->addRow('one'); + $this->assertEquals(['one'], $frame->getRows()); + $this->assertCount(1, $frame); + $frame->addRow('two'); + $this->assertEquals(['one', 'two'], $frame->getRows()); + $this->assertCount(2, $frame); + } + + public function testAll() + { + $frame = new Frame; + $frame->addRow('one'); + $frame->addRows(["two", "three"]); + $frame->newLine(2); + + $this->assertEquals(['one', 'two', 'three', "\n", "\n"], $frame->getRows()); + $this->assertCount(5, $frame); + } +} diff --git a/test/res/testConfirmCanOnlyBeClosedWithEnter.txt b/test/res/testConfirmCanOnlyBeClosedWithEnter.txt new file mode 100644 index 00000000..8d660f00 --- /dev/null +++ b/test/res/testConfirmCanOnlyBeClosedWithEnter.txt @@ -0,0 +1,23 @@ + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + +  + PHP School FTW!  +  +  < OK >   +  + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + diff --git a/test/res/testConfirmWithEvenLengthConfirmAndButton.txt b/test/res/testConfirmWithEvenLengthConfirmAndButton.txt new file mode 100644 index 00000000..614464dc --- /dev/null +++ b/test/res/testConfirmWithEvenLengthConfirmAndButton.txt @@ -0,0 +1,23 @@ + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + +  + PHP School FTW  +  +  < OK >   +  + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + diff --git a/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt b/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt new file mode 100644 index 00000000..770bd618 --- /dev/null +++ b/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt @@ -0,0 +1,23 @@ + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + +  + PHP School FTW  +  +  < OK! >   +  + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + diff --git a/test/res/testConfirmWithOddLengthConfirmAndButton.txt b/test/res/testConfirmWithOddLengthConfirmAndButton.txt new file mode 100644 index 00000000..b77a3d67 --- /dev/null +++ b/test/res/testConfirmWithOddLengthConfirmAndButton.txt @@ -0,0 +1,23 @@ + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + +  + PHP School FTW!  +  +  < OK! >   +  + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + diff --git a/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt b/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt new file mode 100644 index 00000000..8d660f00 --- /dev/null +++ b/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt @@ -0,0 +1,23 @@ + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + +  + PHP School FTW!  +  +  < OK >   +  + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + diff --git a/test/res/testFlashCanBeClosedWithAnyKey.txt b/test/res/testFlashCanBeClosedWithAnyKey.txt new file mode 100644 index 00000000..c06ba9ae --- /dev/null +++ b/test/res/testFlashCanBeClosedWithAnyKey.txt @@ -0,0 +1,21 @@ + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + +  + PHP School FTW!  +  + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + diff --git a/test/res/testFlashWithEvenLength.txt b/test/res/testFlashWithEvenLength.txt new file mode 100644 index 00000000..6fba08a7 --- /dev/null +++ b/test/res/testFlashWithEvenLength.txt @@ -0,0 +1,21 @@ + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + +  + PHP School FTW  +  + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + diff --git a/test/res/testFlashWithOddLength.txt b/test/res/testFlashWithOddLength.txt new file mode 100644 index 00000000..c06ba9ae --- /dev/null +++ b/test/res/testFlashWithOddLength.txt @@ -0,0 +1,21 @@ + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + +  + PHP School FTW!  +  + + +   +  PHP School FTW  +  ==========================================  +  ● Item 1  +   + + From 4382dbd0d7fc40a3c801e14079bb425bbea2f730 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Thu, 27 Oct 2016 20:44:57 +0100 Subject: [PATCH 2/5] Readme updates --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++ examples/confirm.php | 3 --- examples/flash.php | 8 +++--- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 48441871..8c2084a2 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,12 @@ Check out the [examples](examples) directory and run them to check out what is p ##### Disabled Items & Submenus submenu +##### Flash Dialogue +submenu + +##### Confirm Dialogue +submenu + ### API The `CliMenu` object is constructed via the Builder class @@ -412,6 +418,62 @@ $menu = (new CliMenuBuilder) $menu->open(); ``` +#### Dialogues + +##### Flash + +Show a one line message over the top of the menu. It has a separate style object which is colored by default different +to the menu. It can be modified to suit your own style. The dialogue is dismissed with any key press. In the example +below we change the background color on the flash to green. + +```php +use PhpSchool\CliMenu\CliMenu; +use PhpSchool\CliMenu\CliMenuBuilder; + +$itemCallable = function (CliMenu $menu) { + $flash = $menu->flash("PHP School FTW!!"); + $flash->getStyle()->setBg('green'); + $flash->display(); +}; + +$menu = (new CliMenuBuilder) + ->setTitle('Basic CLI Menu') + ->addItem('First Item', $itemCallable) + ->addItem('Second Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addLineBreak('-') + ->build(); + +$menu->open(); +``` + +##### Confirm + +Prompts are very similar to flashes except that a button is shown which has to be selected to dismiss them. The button +text can be customised. + +```php +use PhpSchool\CliMenu\CliMenu; +use PhpSchool\CliMenu\CliMenuBuilder; + +require_once(__DIR__ . '/../vendor/autoload.php'); + +$itemCallable = function (CliMenu $menu) { + $menu->confirm('PHP School FTW!') + ->display('OK!'); +}; + +$menu = (new CliMenuBuilder) + ->setTitle('Basic CLI Menu') + ->addItem('First Item', $itemCallable) + ->addItem('Second Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addLineBreak('-') + ->build(); + +$menu->open(); +``` + --- Once you get going you might just end up with something that looks a little like this... diff --git a/examples/confirm.php b/examples/confirm.php index 2d34b447..1b901aa4 100644 --- a/examples/confirm.php +++ b/examples/confirm.php @@ -15,9 +15,6 @@ ->addItem('First Item', $itemCallable) ->addItem('Second Item', $itemCallable) ->addItem('Third Item', $itemCallable) - ->addItem('Third Item', $itemCallable) - ->addItem('Third Item', $itemCallable) - ->addItem('Third Item', $itemCallable) ->addLineBreak('-') ->build(); diff --git a/examples/flash.php b/examples/flash.php index 8d362743..1196e293 100644 --- a/examples/flash.php +++ b/examples/flash.php @@ -6,8 +6,9 @@ require_once(__DIR__ . '/../vendor/autoload.php'); $itemCallable = function (CliMenu $menu) { - $menu->flash("PHP School FTW!!") - ->display(); + $flash = $menu->flash("PHP School FTW!!"); + $flash->getStyle()->setBg('green'); + $flash->display(); }; $menu = (new CliMenuBuilder) @@ -15,9 +16,6 @@ ->addItem('First Item', $itemCallable) ->addItem('Second Item', $itemCallable) ->addItem('Third Item', $itemCallable) - ->addItem('Third Item', $itemCallable) - ->addItem('Third Item', $itemCallable) - ->addItem('Third Item', $itemCallable) ->addLineBreak('-') ->build(); From e9b166b2c751497c87850ffdc50702d369e7ee76 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Thu, 27 Oct 2016 21:03:26 +0100 Subject: [PATCH 3/5] CS --- src/Frame.php | 2 +- src/Terminal/UnixTerminal.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Frame.php b/src/Frame.php index 93a571bd..398e0dd7 100644 --- a/src/Frame.php +++ b/src/Frame.php @@ -36,7 +36,7 @@ public function addRows(array $rows = []) } /** - * @param $row + * @param string $row */ public function addRow($row) { diff --git a/src/Terminal/UnixTerminal.php b/src/Terminal/UnixTerminal.php index a395db31..d937cb2d 100644 --- a/src/Terminal/UnixTerminal.php +++ b/src/Terminal/UnixTerminal.php @@ -217,6 +217,11 @@ public function moveCursorToRow($rowNumber) echo sprintf("\033[%d;0H", $rowNumber); } + /** + * Move the cursor to the start of a specific column + * + * @param int $column + */ public function moveCursorToColumn($column) { echo sprintf("\033[%dC", $column); From fff333f6bdc959c840b8d2994965cab428d1463e Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Thu, 27 Oct 2016 22:21:47 +0100 Subject: [PATCH 4/5] Fix centering --- src/Dialogue/Confirm.php | 8 ++++++-- src/Dialogue/Dialogue.php | 16 ++++++++++++---- src/Dialogue/Flash.php | 4 +++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/Dialogue/Confirm.php b/src/Dialogue/Confirm.php index 84003c1a..9ac300ce 100644 --- a/src/Dialogue/Confirm.php +++ b/src/Dialogue/Confirm.php @@ -24,9 +24,11 @@ public function display($confirmText = 'OK') $this->emptyRow(); $this->write(sprintf( - "%s %s %s\n", + "%s%s%s%s%s\n", $this->style->getUnselectedSetCode(), + str_repeat(' ', $this->style->getPadding()), $this->text, + str_repeat(' ', $this->style->getPadding()), $this->style->getUnselectedUnsetCode() )); @@ -63,9 +65,11 @@ public function display($confirmText = 'OK') ); $this->write(sprintf( - "%s %s %s\n", + "%s%s%s%s%s\n", $this->style->getUnselectedSetCode(), + str_repeat(' ', $this->style->getPadding()), str_repeat(' ', mb_strlen($this->text)), + str_repeat(' ', $this->style->getPadding()), $this->style->getUnselectedUnsetCode() )); diff --git a/src/Dialogue/Dialogue.php b/src/Dialogue/Dialogue.php index 93ea4464..d48f0210 100644 --- a/src/Dialogue/Dialogue.php +++ b/src/Dialogue/Dialogue.php @@ -73,9 +73,15 @@ protected function assertMenuOpen() */ protected function calculateCoordinates() { - $textLines = count(explode("\n", $this->text)) + 2; - $this->y = ceil(($this->parentMenu->getCurrentFrame()->count() / 2)) - ceil($textLines / 2) + 1; - $this->x = ($this->style->getWidth() / 2) - (mb_strlen($this->text) / 2); + //y + $textLines = count(explode("\n", $this->text)) + 2; + $this->y = ceil($this->parentMenu->getCurrentFrame()->count() / 2) - ceil($textLines / 2) + 1; + + //x + $parentStyle = $this->parentMenu->getStyle(); + $dialogueHalfLength = (mb_strlen($this->text) + ($this->style->getPadding() * 2)) / 2; + $widthHalfLength = ceil($parentStyle->getWidth() / 2); + $this->x = $widthHalfLength - $dialogueHalfLength; } /** @@ -85,9 +91,11 @@ protected function emptyRow() { $this->write( sprintf( - "%s %s %s\n", + "%s%s%s%s%s\n", $this->style->getUnselectedSetCode(), + str_repeat(' ', $this->style->getPadding()), str_repeat(' ', mb_strlen($this->text)), + str_repeat(' ', $this->style->getPadding()), $this->style->getUnselectedUnsetCode() ) ); diff --git a/src/Dialogue/Flash.php b/src/Dialogue/Flash.php index 25025b46..455bb4e1 100644 --- a/src/Dialogue/Flash.php +++ b/src/Dialogue/Flash.php @@ -20,9 +20,11 @@ public function display() $this->emptyRow(); $this->write(sprintf( - "%s %s %s\n", + "%s%s%s%s%s\n", $this->style->getUnselectedSetCode(), + str_repeat(' ', $this->style->getPadding()), $this->text, + str_repeat(' ', $this->style->getPadding()), $this->style->getUnselectedUnsetCode() )); From 85368715ed26e3ae0f4bd9dfc4a39e5224574fca Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Thu, 27 Oct 2016 22:26:53 +0100 Subject: [PATCH 5/5] Update images in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8c2084a2..59d48c99 100644 --- a/README.md +++ b/README.md @@ -89,10 +89,10 @@ Check out the [examples](examples) directory and run them to check out what is p submenu ##### Flash Dialogue -submenu +submenu ##### Confirm Dialogue -submenu +submenu ### API