Skip to content

Commit

Permalink
Merge pull request #283 from bit-willi/feat/handle-control-chars
Browse files Browse the repository at this point in the history
Handle special chars with custom control mappings
  • Loading branch information
AydinHassan authored Oct 23, 2024
2 parents 835c28e + 5a6cec9 commit b1cc7a3
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 16 deletions.
15 changes: 11 additions & 4 deletions src/CliMenu.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,9 @@ private function display() : void
continue;
}

switch ($char->getControl()) {
$controlChar = $char->getControl();

switch ($controlChar) {
case InputCharacter::UP:
case InputCharacter::DOWN:
$this->moveSelectionVertically($char->getControl());
Expand All @@ -310,6 +312,11 @@ private function display() : void
case InputCharacter::ENTER:
$this->executeCurrentItem();
break;
default:
if (isset($this->customControlMappings[$controlChar])) {
$this->customControlMappings[$controlChar]($this);
}
break;
}
}
unset($reader);
Expand Down Expand Up @@ -374,7 +381,7 @@ protected function moveSelectionHorizontally(string $direction) : void
: (int) reset($itemKeys);
}
} while (!$item->canSelectIndex($selectedItemIndex));

$item->setSelectedItemIndex($selectedItemIndex);
}

Expand Down Expand Up @@ -541,7 +548,7 @@ protected function draw() : void
protected function drawMenuItem(MenuItemInterface $item, bool $selected = false) : array
{
$rows = $item->getRows($this->style, $selected);

if ($item instanceof SplitItem) {
$selected = false;
}
Expand Down Expand Up @@ -586,7 +593,7 @@ public function open() : void
if ($this->isOpen()) {
return;
}

if (count($this->items) === 0) {
throw new \RuntimeException('Menu must have at least 1 item before it can be opened');
}
Expand Down
45 changes: 33 additions & 12 deletions test/CliMenuTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public function testSimpleOpenCloseWithDifferentXAndYPadding() : void

self::assertStringEqualsFile($this->getTestFile(), $this->output->fetch());
}

public function testReDrawReDrawsImmediately() : void
{
$this->terminal->expects($this->once())
Expand Down Expand Up @@ -242,11 +242,11 @@ public function testRedrawClearsTerminalFirstIfOptionIsPassed() : void
->will($this->returnCallback(function ($buffer) {
$this->output->write($buffer);
}));

$terminal->expects($this->exactly(3))
->method('read')
->willReturn("\n", "\n", "\n");

$terminal->expects($this->atLeast(2))
->method('clear');

Expand All @@ -264,11 +264,11 @@ public function testRedrawClearsTerminalFirstIfOptionIsPassed() : void
$menu->getStyle()->setWidth(70);
$menu->redraw(true);
}

if ($hits === 2) {
$menu->close();
}

$hits++;
});

Expand Down Expand Up @@ -371,7 +371,7 @@ public function testOpenThrowsExceptionIfNoItemsInMenu() : void
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Menu must have at least 1 item before it can be opened');

(new CliMenu('PHP School FTW', [], $this->terminal))->open();
}

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

$this->assertCount(2, $menu->getItems());

$menu->setItems([$item3, $item4]);

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


public function testAddCustomControlMappingWithControlChar() : void
{
$this->terminal->expects($this->once())
->method('read')
->willReturn("\e");

$style = $this->getStyle($this->terminal);

$action = function (CliMenu $menu) {
$menu->close();
};
$item = new SelectableItem('Item 1', $action);

$menu = new CliMenu('PHP School FTW', [$item], $this->terminal, $style);
$menu->addCustomControlMapping('ESC', $action);
$menu->open();

self::assertStringEqualsFile($this->getTestFile(), $this->output->fetch());
}

public function testAddCustomControlMappingsThrowsExceptionWhenOverwritingExistingDefaultControls() : void
{
$this->expectException(\InvalidArgumentException::class);
Expand Down Expand Up @@ -675,7 +696,7 @@ public function testRemoveCustomControlMapping() : void
$menu = new CliMenu('PHP School FTW', [], $this->terminal);
$menu->addCustomControlMapping('c', $action);
self::assertSame(['c' => $action], $menu->getCustomControlMappings());

$menu->removeCustomControlMapping('c');
self::assertSame([], $menu->getCustomControlMappings());
}
Expand All @@ -685,16 +706,16 @@ public function testSplitItemWithNoSelectableItemsScrollingVertically() : void
$this->terminal->expects($this->exactly(3))
->method('read')
->willReturn("\033[B", "\033[B", "\n");

$action = function (CliMenu $menu) {
$menu->close();
};

$menu = new CliMenu('PHP School FTW', [], $this->terminal);
$menu->addItem(new SelectableItem('One', $action));
$menu->addItem(new SplitItem([new StaticItem('Two'), new StaticItem('Three')]));
$menu->addItem(new SelectableItem('Four', $action));

$menu->open();

self::assertStringEqualsFile($this->getTestFile(), $this->output->fetch());
Expand Down Expand Up @@ -841,7 +862,7 @@ public function testSelectableCallableReceivesSelectableAndNotSplitItem() : void
)
);
$menu->open();

self::assertSame($expectedSelectedItem, $actualSelectedItem);
}

Expand Down
9 changes: 9 additions & 0 deletions test/res/testAddCustomControlMappingWithControlChar.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@


 
 PHP School FTW 
 ======================================== 
 ● Item 1 
 


0 comments on commit b1cc7a3

Please sign in to comment.