Skip to content

Commit 2a2e6ed

Browse files
committed
Refactor to use php-school/terminal
1 parent fdcf19e commit 2a2e6ed

19 files changed

+443
-237
lines changed

src/CliMenu.php

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,20 @@
1616
use PhpSchool\CliMenu\Dialogue\Confirm;
1717
use PhpSchool\CliMenu\Dialogue\Flash;
1818
use PhpSchool\CliMenu\Terminal\TerminalFactory;
19-
use PhpSchool\CliMenu\Terminal\TerminalInterface;
2019
use PhpSchool\CliMenu\Util\StringUtil as s;
20+
use PhpSchool\Terminal\Exception\NotInteractiveTerminal;
21+
use PhpSchool\Terminal\InputCharacter;
22+
use PhpSchool\Terminal\NonCanonicalReader;
23+
use PhpSchool\Terminal\Terminal;
24+
use PhpSchool\Terminal\TerminalReader;
2125

2226
/**
2327
* @author Michael Woodward <[email protected]>
2428
*/
2529
class CliMenu
2630
{
2731
/**
28-
* @var TerminalInterface
32+
* @var Terminal
2933
*/
3034
protected $terminal;
3135

@@ -67,7 +71,7 @@ class CliMenu
6771
public function __construct(
6872
?string $title,
6973
array $items,
70-
TerminalInterface $terminal = null,
74+
Terminal $terminal = null,
7175
MenuStyle $style = null
7276
) {
7377
$this->title = $title;
@@ -80,40 +84,33 @@ public function __construct(
8084

8185
/**
8286
* Configure the terminal to work with CliMenu
83-
*
84-
* @throws InvalidTerminalException
8587
*/
8688
protected function configureTerminal() : void
8789
{
8890
$this->assertTerminalIsValidTTY();
8991

90-
$this->terminal->setCanonicalMode();
92+
$this->terminal->disableCanonicalMode();
93+
$this->terminal->disableEchoBack();
9194
$this->terminal->disableCursor();
9295
$this->terminal->clear();
9396
}
9497

9598
/**
9699
* Revert changes made to the terminal
97-
*
98-
* @throws InvalidTerminalException
99100
*/
100101
protected function tearDownTerminal() : void
101102
{
102-
$this->assertTerminalIsValidTTY();
103-
104-
$this->terminal->setCanonicalMode(false);
105-
$this->terminal->enableCursor();
103+
$this->terminal->restoreOriginalConfiguration();
106104
}
107105

108106
private function assertTerminalIsValidTTY() : void
109107
{
110-
if (!$this->terminal->isTTY()) {
111-
throw new InvalidTerminalException(
112-
sprintf('Terminal "%s" is not a valid TTY', $this->terminal->getDetails())
113-
);
108+
if (!$this->terminal->isInteractive()) {
109+
throw new InvalidTerminalException('Terminal is not interactive (TTY)');
114110
}
115111
}
116112

113+
117114
public function setParent(CliMenu $parent) : void
118115
{
119116
$this->parent = $parent;
@@ -124,7 +121,7 @@ public function getParent() : ?CliMenu
124121
return $this->parent;
125122
}
126123

127-
public function getTerminal() : TerminalInterface
124+
public function getTerminal() : Terminal
128125
{
129126
return $this->terminal;
130127
}
@@ -166,14 +163,28 @@ private function display() : void
166163
{
167164
$this->draw();
168165

169-
while ($this->isOpen() && $input = $this->terminal->getKeyedInput()) {
170-
switch ($input) {
171-
case 'up':
172-
case 'down':
173-
$this->moveSelection($input);
166+
$reader = new NonCanonicalReader($this->terminal);
167+
$reader->addControlMappings([
168+
'^P' => InputCharacter::UP,
169+
'k' => InputCharacter::UP,
170+
'^K' => InputCharacter::DOWN,
171+
'j' => InputCharacter::DOWN,
172+
"\r" => InputCharacter::ENTER,
173+
' ' => InputCharacter::ENTER,
174+
]);
175+
176+
while ($this->isOpen() && $char = $reader->readCharacter()) {
177+
if ($char->isNotControl()) {
178+
continue;
179+
}
180+
181+
switch ($char->getControl()) {
182+
case InputCharacter::UP:
183+
case InputCharacter::DOWN:
184+
$this->moveSelection($char->getControl());
174185
$this->draw();
175186
break;
176-
case 'enter':
187+
case InputCharacter::ENTER:
177188
$this->executeCurrentItem();
178189
break;
179190
}
@@ -188,12 +199,12 @@ protected function moveSelection(string $direction) : void
188199
do {
189200
$itemKeys = array_keys($this->items);
190201

191-
$direction === 'up'
202+
$direction === 'UP'
192203
? $this->selectedItem--
193204
: $this->selectedItem++;
194205

195206
if (!array_key_exists($this->selectedItem, $this->items)) {
196-
$this->selectedItem = $direction === 'up'
207+
$this->selectedItem = $direction === 'UP'
197208
? end($itemKeys)
198209
: reset($itemKeys);
199210
} elseif ($this->getSelectedItem()->canSelect()) {
@@ -224,12 +235,16 @@ protected function executeCurrentItem() : void
224235
* Redraw the menu
225236
*/
226237
public function redraw() : void
238+
{
239+
$this->assertOpen();
240+
$this->draw();
241+
}
242+
243+
private function assertOpen() : void
227244
{
228245
if (!$this->isOpen()) {
229246
throw new MenuNotOpenException;
230247
}
231-
232-
$this->draw();
233248
}
234249

235250
/**
@@ -259,7 +274,7 @@ protected function draw() : void
259274
$frame->newLine(2);
260275

261276
foreach ($frame->getRows() as $row) {
262-
$this->terminal->getOutput()->write($row);
277+
$this->terminal->write($row);
263278
}
264279

265280
$this->currentFrame = $frame;
@@ -282,7 +297,7 @@ protected function drawMenuItem(MenuItemInterface $item, bool $selected = false)
282297

283298
return array_map(function ($row) use ($setColour, $unsetColour) {
284299
return sprintf(
285-
"%s%s%s%s%s%s%s\n\r",
300+
"%s%s%s%s%s%s%s\n",
286301
str_repeat(' ', $this->style->getMargin()),
287302
$setColour,
288303
str_repeat(' ', $this->style->getPadding()),
@@ -386,29 +401,35 @@ public function confirm($text) : Confirm
386401

387402
public function askNumber() : Number
388403
{
404+
$this->assertOpen();
405+
389406
$style = (new MenuStyle($this->terminal))
390407
->setBg('yellow')
391408
->setFg('red');
392409

393-
return new Number(new InputIO($this, $style, $this->terminal));
410+
return new Number(new InputIO($this, $this->terminal), $style);
394411
}
395412

396413
public function askText() : Text
397414
{
415+
$this->assertOpen();
416+
398417
$style = (new MenuStyle($this->terminal))
399418
->setBg('yellow')
400419
->setFg('red');
401420

402-
return new Text(new InputIO($this, $style, $this->terminal));
421+
return new Text(new InputIO($this, $this->terminal), $style);
403422
}
404423

405424
public function askPassword() : Password
406425
{
426+
$this->assertOpen();
427+
407428
$style = (new MenuStyle($this->terminal))
408429
->setBg('yellow')
409430
->setFg('red');
410431

411-
return new Password(new InputIO($this, $style, $this->terminal));
432+
return new Password(new InputIO($this, $this->terminal), $style);
412433
}
413434

414435
private function guardSingleLine($text)

src/CliMenuBuilder.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
use PhpSchool\CliMenu\MenuItem\SelectableItem;
1212
use PhpSchool\CliMenu\MenuItem\StaticItem;
1313
use PhpSchool\CliMenu\Terminal\TerminalFactory;
14-
use PhpSchool\CliMenu\Terminal\TerminalInterface;
1514
use Assert\Assertion;
15+
use PhpSchool\Terminal\Terminal;
1616
use RuntimeException;
1717

1818
/**
@@ -62,7 +62,7 @@ class CliMenuBuilder
6262
private $style;
6363

6464
/**
65-
* @var TerminalInterface
65+
* @var Terminal
6666
*/
6767
private $terminal;
6868

@@ -262,7 +262,7 @@ public function setTitleSeparator(string $separator) : self
262262
return $this;
263263
}
264264

265-
public function setTerminal(TerminalInterface $terminal) : self
265+
public function setTerminal(Terminal $terminal) : self
266266
{
267267
$this->terminal = $terminal;
268268
return $this;

src/Dialogue/Confirm.php

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace PhpSchool\CliMenu\Dialogue;
44

5+
use PhpSchool\Terminal\InputCharacter;
6+
use PhpSchool\Terminal\NonCanonicalReader;
7+
58
/**
69
* @author Aydin Hassan <[email protected]>
710
*/
@@ -33,35 +36,21 @@ public function display(string $confirmText = 'OK') : void
3336
$this->emptyRow();
3437

3538
$confirmText = sprintf(' < %s > ', $confirmText);
36-
$leftFill = ($promptWidth / 2) - (mb_strlen($confirmText) / 2);
39+
$leftFill = ($promptWidth / 2) - (mb_strlen($confirmText) / 2);
3740

3841
$this->write(sprintf(
39-
'%s%s%s',
42+
"%s%s%s%s%s%s%s%s%s\n",
4043
$this->style->getUnselectedSetCode(),
4144
str_repeat(' ', $leftFill),
42-
$this->style->getUnselectedSetCode()
45+
$this->style->getUnselectedUnsetCode(),
46+
$this->style->getSelectedSetCode(),
47+
$confirmText,
48+
$this->style->getSelectedUnsetCode(),
49+
$this->style->getUnselectedSetCode(),
50+
str_repeat(' ', ceil($promptWidth - $leftFill - mb_strlen($confirmText))),
51+
$this->style->getUnselectedUnsetCode()
4352
));
4453

45-
$this->write(
46-
sprintf(
47-
'%s%s%s',
48-
$this->style->getSelectedSetCode(),
49-
$confirmText,
50-
$this->style->getSelectedUnsetCode()
51-
),
52-
-1
53-
);
54-
55-
$this->write(
56-
sprintf(
57-
"%s%s%s\n",
58-
$this->style->getUnselectedSetCode(),
59-
str_repeat(' ', ceil($promptWidth - $leftFill - mb_strlen($confirmText))),
60-
$this->style->getSelectedUnsetCode()
61-
),
62-
-1
63-
);
64-
6554
$this->write(sprintf(
6655
"%s%s%s%s%s\n",
6756
$this->style->getUnselectedSetCode(),
@@ -72,12 +61,14 @@ public function display(string $confirmText = 'OK') : void
7261
));
7362

7463
$this->terminal->moveCursorToTop();
75-
$input = $this->terminal->getKeyedInput();
7664

77-
while ($input !== 'enter') {
78-
$input = $this->terminal->getKeyedInput();
79-
}
65+
$reader = new NonCanonicalReader($this->terminal);
8066

81-
$this->parentMenu->redraw();
67+
while ($char = $reader->readCharacter()) {
68+
if ($char->isControl() && $char->getControl() === InputCharacter::ENTER) {
69+
$this->parentMenu->redraw();
70+
return;
71+
}
72+
}
8273
}
8374
}

src/Dialogue/Dialogue.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use PhpSchool\CliMenu\CliMenu;
66
use PhpSchool\CliMenu\Exception\MenuNotOpenException;
77
use PhpSchool\CliMenu\MenuStyle;
8-
use PhpSchool\CliMenu\Terminal\TerminalInterface;
8+
use PhpSchool\Terminal\Terminal;
99

1010
/**
1111
* @author Aydin Hassan <[email protected]>
@@ -23,7 +23,7 @@ abstract class Dialogue
2323
protected $parentMenu;
2424

2525
/**
26-
* @var TerminalInterface
26+
* @var Terminal
2727
*/
2828
protected $terminal;
2929

@@ -42,7 +42,7 @@ abstract class Dialogue
4242
*/
4343
protected $y;
4444

45-
public function __construct(CliMenu $parentMenu, MenuStyle $menuStyle, TerminalInterface $terminal, string $text)
45+
public function __construct(CliMenu $parentMenu, MenuStyle $menuStyle, Terminal $terminal, string $text)
4646
{
4747
$this->style = $menuStyle;
4848
$this->terminal = $terminal;
@@ -101,7 +101,7 @@ protected function emptyRow() : void
101101
protected function write(string $text, int $column = null) : void
102102
{
103103
$this->terminal->moveCursorToColumn($column ?: $this->x);
104-
$this->terminal->getOutput()->write($text);
104+
$this->terminal->write($text);
105105
}
106106

107107
public function getStyle() : MenuStyle

src/Dialogue/Flash.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace PhpSchool\CliMenu\Dialogue;
44

5+
use PhpSchool\Terminal\NonCanonicalReader;
6+
57
/**
68
* @author Aydin Hassan <[email protected]>
79
*/
@@ -29,8 +31,12 @@ public function display() : void
2931
));
3032

3133
$this->emptyRow();
34+
3235
$this->terminal->moveCursorToTop();
33-
$this->terminal->getKeyedInput();
36+
37+
$reader = new NonCanonicalReader($this->terminal);
38+
$reader->readCharacter();
39+
3440
$this->parentMenu->redraw();
3541
}
3642
}

src/Input/Input.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace PhpSchool\CliMenu\Input;
44

5+
use PhpSchool\CliMenu\MenuStyle;
6+
57
/**
68
* @author Aydin Hassan <[email protected]>
79
*/
@@ -24,4 +26,6 @@ public function setPlaceholderText(string $placeholderText) : Input;
2426
public function getPlaceholderText() : string;
2527

2628
public function filter(string $value) : string;
29+
30+
public function getStyle() : MenuStyle;
2731
}

0 commit comments

Comments
 (0)