Skip to content

Commit b1cc7a3

Browse files
authored
Merge pull request #283 from bit-willi/feat/handle-control-chars
Handle special chars with custom control mappings
2 parents 835c28e + 5a6cec9 commit b1cc7a3

File tree

3 files changed

+53
-16
lines changed

3 files changed

+53
-16
lines changed

src/CliMenu.php

+11-4
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,9 @@ private function display() : void
296296
continue;
297297
}
298298

299-
switch ($char->getControl()) {
299+
$controlChar = $char->getControl();
300+
301+
switch ($controlChar) {
300302
case InputCharacter::UP:
301303
case InputCharacter::DOWN:
302304
$this->moveSelectionVertically($char->getControl());
@@ -310,6 +312,11 @@ private function display() : void
310312
case InputCharacter::ENTER:
311313
$this->executeCurrentItem();
312314
break;
315+
default:
316+
if (isset($this->customControlMappings[$controlChar])) {
317+
$this->customControlMappings[$controlChar]($this);
318+
}
319+
break;
313320
}
314321
}
315322
unset($reader);
@@ -374,7 +381,7 @@ protected function moveSelectionHorizontally(string $direction) : void
374381
: (int) reset($itemKeys);
375382
}
376383
} while (!$item->canSelectIndex($selectedItemIndex));
377-
384+
378385
$item->setSelectedItemIndex($selectedItemIndex);
379386
}
380387

@@ -541,7 +548,7 @@ protected function draw() : void
541548
protected function drawMenuItem(MenuItemInterface $item, bool $selected = false) : array
542549
{
543550
$rows = $item->getRows($this->style, $selected);
544-
551+
545552
if ($item instanceof SplitItem) {
546553
$selected = false;
547554
}
@@ -586,7 +593,7 @@ public function open() : void
586593
if ($this->isOpen()) {
587594
return;
588595
}
589-
596+
590597
if (count($this->items) === 0) {
591598
throw new \RuntimeException('Menu must have at least 1 item before it can be opened');
592599
}

test/CliMenuTest.php

+33-12
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ public function testSimpleOpenCloseWithDifferentXAndYPadding() : void
205205

206206
self::assertStringEqualsFile($this->getTestFile(), $this->output->fetch());
207207
}
208-
208+
209209
public function testReDrawReDrawsImmediately() : void
210210
{
211211
$this->terminal->expects($this->once())
@@ -242,11 +242,11 @@ public function testRedrawClearsTerminalFirstIfOptionIsPassed() : void
242242
->will($this->returnCallback(function ($buffer) {
243243
$this->output->write($buffer);
244244
}));
245-
245+
246246
$terminal->expects($this->exactly(3))
247247
->method('read')
248248
->willReturn("\n", "\n", "\n");
249-
249+
250250
$terminal->expects($this->atLeast(2))
251251
->method('clear');
252252

@@ -264,11 +264,11 @@ public function testRedrawClearsTerminalFirstIfOptionIsPassed() : void
264264
$menu->getStyle()->setWidth(70);
265265
$menu->redraw(true);
266266
}
267-
267+
268268
if ($hits === 2) {
269269
$menu->close();
270270
}
271-
271+
272272
$hits++;
273273
});
274274

@@ -371,7 +371,7 @@ public function testOpenThrowsExceptionIfNoItemsInMenu() : void
371371
{
372372
$this->expectException(\RuntimeException::class);
373373
$this->expectExceptionMessage('Menu must have at least 1 item before it can be opened');
374-
374+
375375
(new CliMenu('PHP School FTW', [], $this->terminal))->open();
376376
}
377377

@@ -440,7 +440,7 @@ public function testSetItems() : void
440440
$menu->addItems([$item1, $item2]);
441441

442442
$this->assertCount(2, $menu->getItems());
443-
443+
444444
$menu->setItems([$item3, $item4]);
445445

446446
$this->assertCount(2, $menu->getItems());
@@ -603,6 +603,27 @@ public function testAddCustomControlMapping() : void
603603
self::assertStringEqualsFile($this->getTestFile(), $this->output->fetch());
604604
}
605605

606+
607+
public function testAddCustomControlMappingWithControlChar() : void
608+
{
609+
$this->terminal->expects($this->once())
610+
->method('read')
611+
->willReturn("\e");
612+
613+
$style = $this->getStyle($this->terminal);
614+
615+
$action = function (CliMenu $menu) {
616+
$menu->close();
617+
};
618+
$item = new SelectableItem('Item 1', $action);
619+
620+
$menu = new CliMenu('PHP School FTW', [$item], $this->terminal, $style);
621+
$menu->addCustomControlMapping('ESC', $action);
622+
$menu->open();
623+
624+
self::assertStringEqualsFile($this->getTestFile(), $this->output->fetch());
625+
}
626+
606627
public function testAddCustomControlMappingsThrowsExceptionWhenOverwritingExistingDefaultControls() : void
607628
{
608629
$this->expectException(\InvalidArgumentException::class);
@@ -675,7 +696,7 @@ public function testRemoveCustomControlMapping() : void
675696
$menu = new CliMenu('PHP School FTW', [], $this->terminal);
676697
$menu->addCustomControlMapping('c', $action);
677698
self::assertSame(['c' => $action], $menu->getCustomControlMappings());
678-
699+
679700
$menu->removeCustomControlMapping('c');
680701
self::assertSame([], $menu->getCustomControlMappings());
681702
}
@@ -685,16 +706,16 @@ public function testSplitItemWithNoSelectableItemsScrollingVertically() : void
685706
$this->terminal->expects($this->exactly(3))
686707
->method('read')
687708
->willReturn("\033[B", "\033[B", "\n");
688-
709+
689710
$action = function (CliMenu $menu) {
690711
$menu->close();
691712
};
692-
713+
693714
$menu = new CliMenu('PHP School FTW', [], $this->terminal);
694715
$menu->addItem(new SelectableItem('One', $action));
695716
$menu->addItem(new SplitItem([new StaticItem('Two'), new StaticItem('Three')]));
696717
$menu->addItem(new SelectableItem('Four', $action));
697-
718+
698719
$menu->open();
699720

700721
self::assertStringEqualsFile($this->getTestFile(), $this->output->fetch());
@@ -841,7 +862,7 @@ public function testSelectableCallableReceivesSelectableAndNotSplitItem() : void
841862
)
842863
);
843864
$menu->open();
844-
865+
845866
self::assertSame($expectedSelectedItem, $actualSelectedItem);
846867
}
847868

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
3+
 
4+
 PHP School FTW 
5+
 ======================================== 
6+
 ● Item 1 
7+
 
8+
9+

0 commit comments

Comments
 (0)