From 92ad273e5f5369c0af5cc1dc406fde5caa7ad0f2 Mon Sep 17 00:00:00 2001 From: Joseph Sugamele Date: Sat, 23 Oct 2021 15:38:46 -0400 Subject: [PATCH 1/4] Cancellable Dialog Complete --- src/CliMenu.php | 6 +- src/Dialogue/Confirm.php | 91 +++++++++---- src/Dialogue/Dialogue.php | 15 ++- src/MenuStyle.php | 14 ++ test/Dialogue/ConfirmTest.php | 122 ++++++++++++++++++ .../testConfirmCanOnlyBeClosedWithEnter.txt | 2 +- .../testConfirmCancellableWithLongPrompt.txt | 23 ++++ .../testConfirmCancellableWithShortPrompt.txt | 23 ++++ ...tConfirmWithEvenLengthConfirmAndButton.txt | 2 +- ...ithEvenLengthConfirmAndOddLengthButton.txt | 2 +- ...stConfirmWithOddLengthConfirmAndButton.txt | 2 +- ...ithOddLengthConfirmAndEvenLengthButton.txt | 2 +- 12 files changed, 268 insertions(+), 36 deletions(-) create mode 100644 test/res/testConfirmCancellableWithLongPrompt.txt create mode 100644 test/res/testConfirmCancellableWithShortPrompt.txt diff --git a/src/CliMenu.php b/src/CliMenu.php index 22e3cfa4..85351a75 100644 --- a/src/CliMenu.php +++ b/src/CliMenu.php @@ -694,10 +694,10 @@ public function flash(string $text, MenuStyle $style = null) : Flash ->setBg('yellow') ->setFg('red'); - return new Flash($this, $style, $this->terminal, $text); + return new Flash($this, $style, $this->terminal, $text, false); } - public function confirm(string $text, MenuStyle $style = null) : Confirm + public function confirm(string $text, MenuStyle $style = null, bool $cancellable = false) : Confirm { $this->guardSingleLine($text); @@ -705,7 +705,7 @@ public function confirm(string $text, MenuStyle $style = null) : Confirm ->setBg('yellow') ->setFg('red'); - return new Confirm($this, $style, $this->terminal, $text); + return new Confirm($this, $style, $this->terminal, $text, $cancellable); } public function askNumber(MenuStyle $style = null) : Number diff --git a/src/Dialogue/Confirm.php b/src/Dialogue/Confirm.php index c19a389f..d6172d77 100644 --- a/src/Dialogue/Confirm.php +++ b/src/Dialogue/Confirm.php @@ -10,11 +10,33 @@ */ class Confirm extends Dialogue { + private $confirm = true; /** * Display confirmation with a button with the given text */ - public function display(string $confirmText = 'OK') : void + public function display(string $confirmText = 'OK', string $cancelText = 'Cancel') : bool + { + $this->drawDialog($confirmText, $cancelText); + + $reader = new NonCanonicalReader($this->terminal); + + while ($char = $reader->readCharacter()) { + if ($char->isControl() && $char->getControl() === InputCharacter::ENTER) { + $this->parentMenu->redraw(); + return $this->confirm; + } elseif ($char->isControl() && $char->getControl() === InputCharacter::TAB || + ($char->isControl() && $char->getControl() === InputCharacter::RIGHT && $this->confirm) || + ($char->isControl() && $char->getControl() === InputCharacter::LEFT && !$this->confirm) + ) { + $this->confirm = !$this->confirm; + $this->drawDialog($confirmText, $cancelText); + } + } + return false; + } + + private function drawDialog(string $confirmText = 'OK', string $cancelText = 'Cancel'): void { $this->assertMenuOpen(); @@ -22,6 +44,44 @@ public function display(string $confirmText = 'OK') : void $promptWidth = mb_strlen($this->text) + 4; + $buttonLength = mb_strlen($confirmText) + 6; + if ($this->cancellable) { + $buttonLength += mb_strlen($cancelText) + 7; + } + + $confirmButton = sprintf( + '%s%s < %s > %s%s', + $this->style->getOptionCode($this->confirm ? 'bold' : 'dim'), + $this->style->getInvertedColoursSetCode(), + $confirmText, + $this->style->getInvertedColoursUnsetCode(), + $this->style->getOptionCode($this->confirm ? 'bold' : 'dim', false) + ); + + $cancelButton = sprintf( + '%s%s < %s > %s%s', + $this->style->getOptionCode($this->confirm ? 'dim' : 'bold'), + $this->style->getInvertedColoursSetCode(), + $cancelText, + $this->style->getInvertedColoursUnsetCode(), + $this->style->getOptionCode($this->confirm ? 'dim' : 'bold', false) + ); + + $buttonRow = $confirmButton . ($this->cancellable ? " $cancelButton" : ''); + + if ($promptWidth < $buttonLength) { + $pad = ($buttonLength - $promptWidth) / 2; + $this->text = sprintf( + '%s%s%s', + str_repeat(' ', round($pad, 0, 2) + $this->style->getPaddingLeftRight()), + $this->text, + str_repeat(' ', round($pad, 0, 1) + $this->style->getPaddingLeftRight()) + ); + $promptWidth = mb_strlen($this->text) + 4; + } + + $leftFill = (int) (($promptWidth / 2) - ($buttonLength / 2)); + $this->emptyRow(); $this->write(sprintf( @@ -35,38 +95,17 @@ public function display(string $confirmText = 'OK') : void $this->emptyRow(); - $confirmText = sprintf(' < %s > ', $confirmText); - $leftFill = (int) (($promptWidth / 2) - (mb_strlen($confirmText) / 2)); - $this->write(sprintf( - "%s%s%s%s%s%s%s\n", + "%s%s%s%s%s\n", $this->style->getColoursSetCode(), str_repeat(' ', $leftFill), - $this->style->getInvertedColoursSetCode(), - $confirmText, - $this->style->getInvertedColoursUnsetCode(), - str_repeat(' ', (int) ceil($promptWidth - $leftFill - mb_strlen($confirmText))), + $buttonRow, + str_repeat(' ', (int) ceil($promptWidth - $leftFill - $buttonLength)), $this->style->getColoursResetCode() )); - $this->write(sprintf( - "%s%s%s%s%s\n", - $this->style->getColoursSetCode(), - str_repeat(' ', $this->style->getPaddingLeftRight()), - str_repeat(' ', mb_strlen($this->text)), - str_repeat(' ', $this->style->getPaddingLeftRight()), - $this->style->getColoursResetCode() - )); + $this->emptyRow(); $this->terminal->moveCursorToTop(); - - $reader = new NonCanonicalReader($this->terminal); - - while ($char = $reader->readCharacter()) { - if ($char->isControl() && $char->getControl() === InputCharacter::ENTER) { - $this->parentMenu->redraw(); - return; - } - } } } diff --git a/src/Dialogue/Dialogue.php b/src/Dialogue/Dialogue.php index 5ec0b4f7..f9bbd5a9 100644 --- a/src/Dialogue/Dialogue.php +++ b/src/Dialogue/Dialogue.php @@ -32,6 +32,11 @@ abstract class Dialogue */ protected $text; + /** + * @var bool $cancellable + */ + protected $cancellable; + /** * @var int */ @@ -42,12 +47,18 @@ abstract class Dialogue */ protected $y; - public function __construct(CliMenu $parentMenu, MenuStyle $menuStyle, Terminal $terminal, string $text) - { + public function __construct( + CliMenu $parentMenu, + MenuStyle $menuStyle, + Terminal $terminal, + string $text, + bool $cancellable + ) { $this->style = $menuStyle; $this->terminal = $terminal; $this->text = $text; $this->parentMenu = $parentMenu; + $this->cancellable = $cancellable; $this->calculateCoordinates(); } diff --git a/src/MenuStyle.php b/src/MenuStyle.php index fa318a6b..0113ed36 100644 --- a/src/MenuStyle.php +++ b/src/MenuStyle.php @@ -813,6 +813,20 @@ public function getBorderColourCode() : string return sprintf("\033[%sm", $borderColourCode); } + + /** + * Get ansi escape sequence for setting or unsetting the specified option code. + * + * @param string $string Option code (bold|dim|underscore|blink|reverse|conceal) + * @param bool $set Whether to set or unset the code + * + * @return string + */ + public function getOptionCode(string $string, bool $set = true): string + { + return sprintf("\033[%sm", self::$availableOptions[$string][$set ? 'set' : 'unset']); + } + /** * Get a string of given length consisting of 0-9 * eg $length = 15 : 012345678901234 diff --git a/test/Dialogue/ConfirmTest.php b/test/Dialogue/ConfirmTest.php index 56dd976c..49790df8 100644 --- a/test/Dialogue/ConfirmTest.php +++ b/test/Dialogue/ConfirmTest.php @@ -5,6 +5,7 @@ use PhpSchool\CliMenu\CliMenu; use PhpSchool\CliMenu\MenuItem\SelectableItem; use PhpSchool\CliMenu\MenuStyle; +use PhpSchool\Terminal\InputCharacter; use PhpSchool\Terminal\IO\BufferedOutput; use PhpSchool\Terminal\Terminal; use PHPUnit\Framework\TestCase; @@ -140,6 +141,54 @@ public function testConfirmWithOddLengthConfirmAndEvenLengthButton() : void static::assertStringEqualsFile($this->getTestFile(), $this->output->fetch()); } + public function testConfirmCancellableWithShortPrompt(): void + { + $this->terminal + ->method('read') + ->will($this->onConsecutiveCalls( + "\n", + "\n" + )); + + $style = $this->getStyle($this->terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->confirm('PHP', null, true) + ->display('OK', 'Cancel'); + + $menu->close(); + }); + + $menu = new CliMenu('PHP School FTW', [$item], $this->terminal, $style); + $menu->open(); + + static::assertStringEqualsFile($this->getTestFile(), $this->output->fetch()); + } + + public function testConfirmCancellableWithLongPrompt(): void + { + $this->terminal + ->method('read') + ->will($this->onConsecutiveCalls( + "\n", + "\n" + )); + + $style = $this->getStyle($this->terminal); + + $item = new SelectableItem('Item 1', function (CliMenu $menu) { + $menu->confirm('PHP School Rocks FTW!', null, true) + ->display('OK', 'Cancel'); + + $menu->close(); + }); + + $menu = new CliMenu('PHP School FTW', [$item], $this->terminal, $style); + $menu->open(); + + static::assertStringEqualsFile($this->getTestFile(), $this->output->fetch()); + } + public function testConfirmCanOnlyBeClosedWithEnter() : void { $this->terminal @@ -166,6 +215,79 @@ public function testConfirmCanOnlyBeClosedWithEnter() : void static::assertStringEqualsFile($this->getTestFile(), $this->output->fetch()); } + public function testConfirmOkNonCancellableReturnsTrue() + { + $this->terminal + ->method('read') + ->will($this->onConsecutiveCalls( + "\n", + 'tab', + "\n" + )); + + $style = $this->getStyle($this->terminal); + + $return = ''; + + $item = new SelectableItem('Item 1', function (CliMenu $menu) use (&$return) { + $return = $menu->confirm('PHP School FTW!') + ->display('OK'); + + $menu->close(); + }); + + $menu = new CliMenu('PHP School FTW', [$item], $this->terminal, $style); + $menu->open(); + + static::assertTrue($return); + } + + public function testConfirmOkCancellableReturnsTrue() + { + $this->terminal + ->method('read') + ->willReturn("\n", "\t", "\t", "\n"); + + $style = $this->getStyle($this->terminal); + + $return = ''; + + $item = new SelectableItem('Item 1', function (CliMenu $menu) use (&$return) { + $return = $menu->confirm('PHP School FTW!') + ->display('OK', 'Cancel'); + + $menu->close(); + }); + + $menu = new CliMenu('PHP School FTW', [$item], $this->terminal, $style); + $menu->open(); + + static::assertTrue($return); + } + + public function testConfirmCancelCancellableReturnsFalse() + { + $this->terminal + ->method('read') + ->willReturn("\n", "\t", "\n"); + + $style = $this->getStyle($this->terminal); + + $return = ''; + + $item = new SelectableItem('Item 1', function (CliMenu $menu) use (&$return) { + $return = $menu->confirm('PHP School FTW!', null, true) + ->display('OK', 'Cancel'); + + $menu->close(); + }); + + $menu = new CliMenu('PHP School FTW', [$item], $this->terminal, $style); + $menu->open(); + + static::assertFalse($return); + } + private function getTestFile() : string { return sprintf('%s/../res/%s.txt', __DIR__, $this->getName()); diff --git a/test/res/testConfirmCanOnlyBeClosedWithEnter.txt b/test/res/testConfirmCanOnlyBeClosedWithEnter.txt index 2c4cc81d..39c6a7b4 100644 --- a/test/res/testConfirmCanOnlyBeClosedWithEnter.txt +++ b/test/res/testConfirmCanOnlyBeClosedWithEnter.txt @@ -10,7 +10,7 @@    PHP School FTW!    -  < OK >   +  < OK >     diff --git a/test/res/testConfirmCancellableWithLongPrompt.txt b/test/res/testConfirmCancellableWithLongPrompt.txt new file mode 100644 index 00000000..6ca1dc84 --- /dev/null +++ b/test/res/testConfirmCancellableWithLongPrompt.txt @@ -0,0 +1,23 @@ + + +   +  PHP School FTW  +  ========================================  +  ● Item 1  +   + + +  + PHP School Rocks FTW!  +  +  < OK >   < Cancel >   +  + + +   +  PHP School FTW  +  ========================================  +  ● Item 1  +   + + diff --git a/test/res/testConfirmCancellableWithShortPrompt.txt b/test/res/testConfirmCancellableWithShortPrompt.txt new file mode 100644 index 00000000..9d84fc5a --- /dev/null +++ b/test/res/testConfirmCancellableWithShortPrompt.txt @@ -0,0 +1,23 @@ + + +   +  PHP School FTW  +  ========================================  +  ● Item 1  +   + + +  + PHP  +  +  < OK >   < Cancel >   +  + + +   +  PHP School FTW  +  ========================================  +  ● Item 1  +   + + diff --git a/test/res/testConfirmWithEvenLengthConfirmAndButton.txt b/test/res/testConfirmWithEvenLengthConfirmAndButton.txt index b7882f44..e686857d 100644 --- a/test/res/testConfirmWithEvenLengthConfirmAndButton.txt +++ b/test/res/testConfirmWithEvenLengthConfirmAndButton.txt @@ -10,7 +10,7 @@    PHP School FTW    -  < OK >   +  < OK >     diff --git a/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt b/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt index 42c46284..d00df0ac 100644 --- a/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt +++ b/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt @@ -10,7 +10,7 @@    PHP School FTW    -  < OK! >   +  < OK! >     diff --git a/test/res/testConfirmWithOddLengthConfirmAndButton.txt b/test/res/testConfirmWithOddLengthConfirmAndButton.txt index 0a4dc10e..1b877e94 100644 --- a/test/res/testConfirmWithOddLengthConfirmAndButton.txt +++ b/test/res/testConfirmWithOddLengthConfirmAndButton.txt @@ -10,7 +10,7 @@    PHP School FTW!    -  < OK! >   +  < OK! >     diff --git a/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt b/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt index 2c4cc81d..39c6a7b4 100644 --- a/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt +++ b/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt @@ -10,7 +10,7 @@    PHP School FTW!    -  < OK >   +  < OK >     From 71be47613ce8c3545ee61f7ca192fff6b11739a3 Mon Sep 17 00:00:00 2001 From: Joseph Sugamele Date: Sat, 23 Oct 2021 15:46:30 -0400 Subject: [PATCH 2/4] Added example --- examples/confirm-cancellable.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 examples/confirm-cancellable.php diff --git a/examples/confirm-cancellable.php b/examples/confirm-cancellable.php new file mode 100644 index 00000000..22481230 --- /dev/null +++ b/examples/confirm-cancellable.php @@ -0,0 +1,29 @@ +confirm('PHP School FTW!', null, true) + ->display('OK', 'Cancel'); + + if ($continue) { + // Something Destructive + echo "OK"; + } else { + // Do nothing + echo "Cancel"; + } +}; + +$menu = (new CliMenuBuilder) + ->setTitle('Basic CLI Menu') + ->addItem('First Item', $itemCallable) + ->addItem('Second Item', $itemCallable) + ->addItem('Third Item', $itemCallable) + ->addLineBreak('-') + ->build(); + +$menu->open(); From b17b5445f3e39339a94255a3d9760c000d4adcc3 Mon Sep 17 00:00:00 2001 From: Joseph Sugamele Date: Sat, 23 Oct 2021 22:55:44 -0400 Subject: [PATCH 3/4] PHPStan Updates. --- src/Dialogue/Confirm.php | 8 +++++--- src/Input/InputIO.php | 2 +- src/MenuStyle.php | 5 +++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Dialogue/Confirm.php b/src/Dialogue/Confirm.php index d6172d77..d1bcb4ec 100644 --- a/src/Dialogue/Confirm.php +++ b/src/Dialogue/Confirm.php @@ -10,6 +10,9 @@ */ class Confirm extends Dialogue { + /** + * @var bool + */ private $confirm = true; /** @@ -33,7 +36,6 @@ public function display(string $confirmText = 'OK', string $cancelText = 'Cancel $this->drawDialog($confirmText, $cancelText); } } - return false; } private function drawDialog(string $confirmText = 'OK', string $cancelText = 'Cancel'): void @@ -73,9 +75,9 @@ private function drawDialog(string $confirmText = 'OK', string $cancelText = 'Ca $pad = ($buttonLength - $promptWidth) / 2; $this->text = sprintf( '%s%s%s', - str_repeat(' ', round($pad, 0, 2) + $this->style->getPaddingLeftRight()), + str_repeat(' ', intval(round($pad, 0, 2) + $this->style->getPaddingLeftRight())), $this->text, - str_repeat(' ', round($pad, 0, 1) + $this->style->getPaddingLeftRight()) + str_repeat(' ', intval(round($pad, 0, 1) + $this->style->getPaddingLeftRight())) ); $promptWidth = mb_strlen($this->text) + 4; } diff --git a/src/Input/InputIO.php b/src/Input/InputIO.php index 2f17af08..fb76ab0d 100644 --- a/src/Input/InputIO.php +++ b/src/Input/InputIO.php @@ -108,7 +108,7 @@ function (string $line) { }, $lines ) - ); + ) ? : 0; } private function calculateYPosition() : int diff --git a/src/MenuStyle.php b/src/MenuStyle.php index 0113ed36..29ce72c8 100644 --- a/src/MenuStyle.php +++ b/src/MenuStyle.php @@ -509,6 +509,7 @@ private function generatePaddingTopBottomRows() : void ); } + $this->paddingTopBottom = $this->paddingTopBottom >= 0 ? $this->paddingTopBottom : 0; $this->paddingTopBottomRows = array_fill(0, $this->paddingTopBottom, $paddingRow); } @@ -663,6 +664,10 @@ private function generateBorderRows() : void ); } + $this->borderTopWidth = $this->borderTopWidth >= 0 ? $this->borderTopWidth : 0; + $this->borderBottomWidth = $this->borderBottomWidth >= 0 ? $this->borderBottomWidth : 0; + + $this->borderTopRows = array_fill(0, $this->borderTopWidth, $borderRow); $this->borderBottomRows = array_fill(0, $this->borderBottomWidth, $borderRow); } From 42a6491a30f91764b4abc737a07068fcf0f48dc1 Mon Sep 17 00:00:00 2001 From: Joseph Sugamele Date: Mon, 22 Nov 2021 15:02:55 -0500 Subject: [PATCH 4/4] Seperated the classes --- examples/confirm-cancellable.php | 2 +- src/CliMenu.php | 18 ++- src/Dialogue/CancellableConfirm.php | 111 ++++++++++++++++++ src/Dialogue/Confirm.php | 93 ++++----------- src/Dialogue/Dialogue.php | 4 +- test/Dialogue/ConfirmTest.php | 11 +- .../testConfirmCanOnlyBeClosedWithEnter.txt | 2 +- ...tConfirmWithEvenLengthConfirmAndButton.txt | 2 +- ...ithEvenLengthConfirmAndOddLengthButton.txt | 2 +- ...stConfirmWithOddLengthConfirmAndButton.txt | 2 +- ...ithOddLengthConfirmAndEvenLengthButton.txt | 2 +- 11 files changed, 164 insertions(+), 85 deletions(-) create mode 100644 src/Dialogue/CancellableConfirm.php diff --git a/examples/confirm-cancellable.php b/examples/confirm-cancellable.php index 22481230..023cf213 100644 --- a/examples/confirm-cancellable.php +++ b/examples/confirm-cancellable.php @@ -6,7 +6,7 @@ require_once(__DIR__ . '/../vendor/autoload.php'); $itemCallable = function (CliMenu $menu) { - $continue = $menu->confirm('PHP School FTW!', null, true) + $continue = $menu->cancellableConfirm('PHP School FTW!', null, true) ->display('OK', 'Cancel'); if ($continue) { diff --git a/src/CliMenu.php b/src/CliMenu.php index 85351a75..0f44d92d 100644 --- a/src/CliMenu.php +++ b/src/CliMenu.php @@ -2,6 +2,7 @@ namespace PhpSchool\CliMenu; +use PhpSchool\CliMenu\Dialogue\CancellableConfirm; use PhpSchool\CliMenu\Exception\InvalidTerminalException; use PhpSchool\CliMenu\Exception\MenuNotOpenException; use PhpSchool\CliMenu\Input\InputIO; @@ -694,10 +695,10 @@ public function flash(string $text, MenuStyle $style = null) : Flash ->setBg('yellow') ->setFg('red'); - return new Flash($this, $style, $this->terminal, $text, false); + return new Flash($this, $style, $this->terminal, $text); } - public function confirm(string $text, MenuStyle $style = null, bool $cancellable = false) : Confirm + public function confirm(string $text, MenuStyle $style = null) : Confirm { $this->guardSingleLine($text); @@ -705,7 +706,18 @@ public function confirm(string $text, MenuStyle $style = null, bool $cancellable ->setBg('yellow') ->setFg('red'); - return new Confirm($this, $style, $this->terminal, $text, $cancellable); + return new Confirm($this, $style, $this->terminal, $text); + } + + public function cancellableConfirm(string $text, MenuStyle $style = null) : CancellableConfirm + { + $this->guardSingleLine($text); + + $style = $style ?? (new MenuStyle($this->terminal)) + ->setBg('yellow') + ->setFg('red'); + + return new CancellableConfirm($this, $style, $this->terminal, $text); } public function askNumber(MenuStyle $style = null) : Number diff --git a/src/Dialogue/CancellableConfirm.php b/src/Dialogue/CancellableConfirm.php new file mode 100644 index 00000000..4680e746 --- /dev/null +++ b/src/Dialogue/CancellableConfirm.php @@ -0,0 +1,111 @@ + + */ +class CancellableConfirm extends Dialogue +{ + /** + * @var bool + */ + private $confirm = true; + + /** + * Display confirmation with a button with the given text + */ + public function display(string $confirmText = 'OK', string $cancelText = 'Cancel') : bool + { + $this->drawDialog($confirmText, $cancelText); + + $reader = new NonCanonicalReader($this->terminal); + + while ($char = $reader->readCharacter()) { + if ($char->isControl() && $char->getControl() === InputCharacter::ENTER) { + $this->parentMenu->redraw(); + return $this->confirm; + } elseif ($char->isControl() && $char->getControl() === InputCharacter::TAB || + ($char->isControl() && $char->getControl() === InputCharacter::RIGHT && $this->confirm) || + ($char->isControl() && $char->getControl() === InputCharacter::LEFT && !$this->confirm) + ) { + $this->confirm = !$this->confirm; + $this->drawDialog($confirmText, $cancelText); + } + } + } + + private function drawDialog(string $confirmText = 'OK', string $cancelText = 'Cancel'): void + { + $this->assertMenuOpen(); + + $this->terminal->moveCursorToRow($this->y); + + $promptWidth = mb_strlen($this->text) + 4; + + $buttonLength = mb_strlen($confirmText) + 6; + $buttonLength += mb_strlen($cancelText) + 7; + + $confirmButton = sprintf( + '%s%s < %s > %s%s', + $this->style->getOptionCode($this->confirm ? 'bold' : 'dim'), + $this->style->getInvertedColoursSetCode(), + $confirmText, + $this->style->getInvertedColoursUnsetCode(), + $this->style->getOptionCode($this->confirm ? 'bold' : 'dim', false) + ); + + $cancelButton = sprintf( + '%s%s < %s > %s%s', + $this->style->getOptionCode($this->confirm ? 'dim' : 'bold'), + $this->style->getInvertedColoursSetCode(), + $cancelText, + $this->style->getInvertedColoursUnsetCode(), + $this->style->getOptionCode($this->confirm ? 'dim' : 'bold', false) + ); + + $buttonRow = $confirmButton . " " . $cancelButton; + + if ($promptWidth < $buttonLength) { + $pad = ($buttonLength - $promptWidth) / 2; + $this->text = sprintf( + '%s%s%s', + str_repeat(' ', intval(round($pad, 0, 2) + $this->style->getPaddingLeftRight())), + $this->text, + str_repeat(' ', intval(round($pad, 0, 1) + $this->style->getPaddingLeftRight())) + ); + $promptWidth = mb_strlen($this->text) + 4; + } + + $leftFill = (int) (($promptWidth / 2) - ($buttonLength / 2)); + + $this->emptyRow(); + + $this->write(sprintf( + "%s%s%s%s%s\n", + $this->style->getColoursSetCode(), + str_repeat(' ', $this->style->getPaddingLeftRight()), + $this->text, + str_repeat(' ', $this->style->getPaddingLeftRight()), + $this->style->getColoursResetCode() + )); + + $this->emptyRow(); + + $this->write(sprintf( + "%s%s%s%s%s\n", + $this->style->getColoursSetCode(), + str_repeat(' ', $leftFill), + $buttonRow, + str_repeat(' ', (int) ceil($promptWidth - $leftFill - $buttonLength)), + $this->style->getColoursResetCode() + )); + + $this->emptyRow(); + + $this->terminal->moveCursorToTop(); + } +} diff --git a/src/Dialogue/Confirm.php b/src/Dialogue/Confirm.php index d1bcb4ec..c19a389f 100644 --- a/src/Dialogue/Confirm.php +++ b/src/Dialogue/Confirm.php @@ -10,35 +10,11 @@ */ class Confirm extends Dialogue { - /** - * @var bool - */ - private $confirm = true; /** * Display confirmation with a button with the given text */ - public function display(string $confirmText = 'OK', string $cancelText = 'Cancel') : bool - { - $this->drawDialog($confirmText, $cancelText); - - $reader = new NonCanonicalReader($this->terminal); - - while ($char = $reader->readCharacter()) { - if ($char->isControl() && $char->getControl() === InputCharacter::ENTER) { - $this->parentMenu->redraw(); - return $this->confirm; - } elseif ($char->isControl() && $char->getControl() === InputCharacter::TAB || - ($char->isControl() && $char->getControl() === InputCharacter::RIGHT && $this->confirm) || - ($char->isControl() && $char->getControl() === InputCharacter::LEFT && !$this->confirm) - ) { - $this->confirm = !$this->confirm; - $this->drawDialog($confirmText, $cancelText); - } - } - } - - private function drawDialog(string $confirmText = 'OK', string $cancelText = 'Cancel'): void + public function display(string $confirmText = 'OK') : void { $this->assertMenuOpen(); @@ -46,44 +22,6 @@ private function drawDialog(string $confirmText = 'OK', string $cancelText = 'Ca $promptWidth = mb_strlen($this->text) + 4; - $buttonLength = mb_strlen($confirmText) + 6; - if ($this->cancellable) { - $buttonLength += mb_strlen($cancelText) + 7; - } - - $confirmButton = sprintf( - '%s%s < %s > %s%s', - $this->style->getOptionCode($this->confirm ? 'bold' : 'dim'), - $this->style->getInvertedColoursSetCode(), - $confirmText, - $this->style->getInvertedColoursUnsetCode(), - $this->style->getOptionCode($this->confirm ? 'bold' : 'dim', false) - ); - - $cancelButton = sprintf( - '%s%s < %s > %s%s', - $this->style->getOptionCode($this->confirm ? 'dim' : 'bold'), - $this->style->getInvertedColoursSetCode(), - $cancelText, - $this->style->getInvertedColoursUnsetCode(), - $this->style->getOptionCode($this->confirm ? 'dim' : 'bold', false) - ); - - $buttonRow = $confirmButton . ($this->cancellable ? " $cancelButton" : ''); - - if ($promptWidth < $buttonLength) { - $pad = ($buttonLength - $promptWidth) / 2; - $this->text = sprintf( - '%s%s%s', - str_repeat(' ', intval(round($pad, 0, 2) + $this->style->getPaddingLeftRight())), - $this->text, - str_repeat(' ', intval(round($pad, 0, 1) + $this->style->getPaddingLeftRight())) - ); - $promptWidth = mb_strlen($this->text) + 4; - } - - $leftFill = (int) (($promptWidth / 2) - ($buttonLength / 2)); - $this->emptyRow(); $this->write(sprintf( @@ -97,17 +35,38 @@ private function drawDialog(string $confirmText = 'OK', string $cancelText = 'Ca $this->emptyRow(); + $confirmText = sprintf(' < %s > ', $confirmText); + $leftFill = (int) (($promptWidth / 2) - (mb_strlen($confirmText) / 2)); + $this->write(sprintf( - "%s%s%s%s%s\n", + "%s%s%s%s%s%s%s\n", $this->style->getColoursSetCode(), str_repeat(' ', $leftFill), - $buttonRow, - str_repeat(' ', (int) ceil($promptWidth - $leftFill - $buttonLength)), + $this->style->getInvertedColoursSetCode(), + $confirmText, + $this->style->getInvertedColoursUnsetCode(), + str_repeat(' ', (int) ceil($promptWidth - $leftFill - mb_strlen($confirmText))), $this->style->getColoursResetCode() )); - $this->emptyRow(); + $this->write(sprintf( + "%s%s%s%s%s\n", + $this->style->getColoursSetCode(), + str_repeat(' ', $this->style->getPaddingLeftRight()), + str_repeat(' ', mb_strlen($this->text)), + str_repeat(' ', $this->style->getPaddingLeftRight()), + $this->style->getColoursResetCode() + )); $this->terminal->moveCursorToTop(); + + $reader = new NonCanonicalReader($this->terminal); + + while ($char = $reader->readCharacter()) { + if ($char->isControl() && $char->getControl() === InputCharacter::ENTER) { + $this->parentMenu->redraw(); + return; + } + } } } diff --git a/src/Dialogue/Dialogue.php b/src/Dialogue/Dialogue.php index f9bbd5a9..d6f60240 100644 --- a/src/Dialogue/Dialogue.php +++ b/src/Dialogue/Dialogue.php @@ -51,14 +51,12 @@ public function __construct( CliMenu $parentMenu, MenuStyle $menuStyle, Terminal $terminal, - string $text, - bool $cancellable + string $text ) { $this->style = $menuStyle; $this->terminal = $terminal; $this->text = $text; $this->parentMenu = $parentMenu; - $this->cancellable = $cancellable; $this->calculateCoordinates(); } diff --git a/test/Dialogue/ConfirmTest.php b/test/Dialogue/ConfirmTest.php index 49790df8..c91d955b 100644 --- a/test/Dialogue/ConfirmTest.php +++ b/test/Dialogue/ConfirmTest.php @@ -5,7 +5,6 @@ use PhpSchool\CliMenu\CliMenu; use PhpSchool\CliMenu\MenuItem\SelectableItem; use PhpSchool\CliMenu\MenuStyle; -use PhpSchool\Terminal\InputCharacter; use PhpSchool\Terminal\IO\BufferedOutput; use PhpSchool\Terminal\Terminal; use PHPUnit\Framework\TestCase; @@ -153,7 +152,7 @@ public function testConfirmCancellableWithShortPrompt(): void $style = $this->getStyle($this->terminal); $item = new SelectableItem('Item 1', function (CliMenu $menu) { - $menu->confirm('PHP', null, true) + $menu->cancellableConfirm('PHP', null, true) ->display('OK', 'Cancel'); $menu->close(); @@ -177,7 +176,7 @@ public function testConfirmCancellableWithLongPrompt(): void $style = $this->getStyle($this->terminal); $item = new SelectableItem('Item 1', function (CliMenu $menu) { - $menu->confirm('PHP School Rocks FTW!', null, true) + $menu->cancellableConfirm('PHP School Rocks FTW!', null, true) ->display('OK', 'Cancel'); $menu->close(); @@ -230,7 +229,7 @@ public function testConfirmOkNonCancellableReturnsTrue() $return = ''; $item = new SelectableItem('Item 1', function (CliMenu $menu) use (&$return) { - $return = $menu->confirm('PHP School FTW!') + $return = $menu->cancellableConfirm('PHP School FTW!') ->display('OK'); $menu->close(); @@ -253,7 +252,7 @@ public function testConfirmOkCancellableReturnsTrue() $return = ''; $item = new SelectableItem('Item 1', function (CliMenu $menu) use (&$return) { - $return = $menu->confirm('PHP School FTW!') + $return = $menu->cancellableConfirm('PHP School FTW!') ->display('OK', 'Cancel'); $menu->close(); @@ -276,7 +275,7 @@ public function testConfirmCancelCancellableReturnsFalse() $return = ''; $item = new SelectableItem('Item 1', function (CliMenu $menu) use (&$return) { - $return = $menu->confirm('PHP School FTW!', null, true) + $return = $menu->cancellableConfirm('PHP School FTW!', null) ->display('OK', 'Cancel'); $menu->close(); diff --git a/test/res/testConfirmCanOnlyBeClosedWithEnter.txt b/test/res/testConfirmCanOnlyBeClosedWithEnter.txt index 39c6a7b4..2c4cc81d 100644 --- a/test/res/testConfirmCanOnlyBeClosedWithEnter.txt +++ b/test/res/testConfirmCanOnlyBeClosedWithEnter.txt @@ -10,7 +10,7 @@    PHP School FTW!    -  < OK >   +  < OK >     diff --git a/test/res/testConfirmWithEvenLengthConfirmAndButton.txt b/test/res/testConfirmWithEvenLengthConfirmAndButton.txt index e686857d..b7882f44 100644 --- a/test/res/testConfirmWithEvenLengthConfirmAndButton.txt +++ b/test/res/testConfirmWithEvenLengthConfirmAndButton.txt @@ -10,7 +10,7 @@    PHP School FTW    -  < OK >   +  < OK >     diff --git a/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt b/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt index d00df0ac..42c46284 100644 --- a/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt +++ b/test/res/testConfirmWithEvenLengthConfirmAndOddLengthButton.txt @@ -10,7 +10,7 @@    PHP School FTW    -  < OK! >   +  < OK! >     diff --git a/test/res/testConfirmWithOddLengthConfirmAndButton.txt b/test/res/testConfirmWithOddLengthConfirmAndButton.txt index 1b877e94..0a4dc10e 100644 --- a/test/res/testConfirmWithOddLengthConfirmAndButton.txt +++ b/test/res/testConfirmWithOddLengthConfirmAndButton.txt @@ -10,7 +10,7 @@    PHP School FTW!    -  < OK! >   +  < OK! >     diff --git a/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt b/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt index 39c6a7b4..2c4cc81d 100644 --- a/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt +++ b/test/res/testConfirmWithOddLengthConfirmAndEvenLengthButton.txt @@ -10,7 +10,7 @@    PHP School FTW!    -  < OK >   +  < OK >