- Fix Enter key detection: handle multiple Enter key formats (\n, \r, \r\n) - Reduce flickering: lower render frequency from 60 FPS to 30 FPS - Fix menu bar visibility: re-render menu bar after content to prevent overwriting - Fix content positioning: explicit line positioning for categories and commands - Fix line shifting: clear lines before writing, control newlines manually - Limit visible items: prevent overflow with maxVisibleCategories/Commands - Improve CPU usage: increase sleep interval when no events processed This fixes: - Enter key not working for selection - Strong flickering of the application - Menu bar not visible or being overwritten - Top half of selection list not displayed - Lines being shifted/misaligned
164 lines
3.1 KiB
PHP
164 lines
3.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Framework\Console\Helpers;
|
|
|
|
/**
|
|
* Mock keyboard input for testing interactive menus and TUI components
|
|
*/
|
|
final class MockKeyboard
|
|
{
|
|
private array $keySequence = [];
|
|
|
|
private int $currentIndex = 0;
|
|
|
|
/**
|
|
* ANSI escape sequences for common keys
|
|
*/
|
|
public const KEY_UP = "\033[A";
|
|
public const KEY_DOWN = "\033[B";
|
|
public const KEY_RIGHT = "\033[C";
|
|
public const KEY_LEFT = "\033[D";
|
|
public const KEY_ENTER = "\n";
|
|
public const KEY_ESCAPE = "\033";
|
|
public const KEY_TAB = "\t";
|
|
public const KEY_BACKSPACE = "\x08";
|
|
public const KEY_DELETE = "\033[3~";
|
|
public const KEY_HOME = "\033[H";
|
|
public const KEY_END = "\033[F";
|
|
public const KEY_PAGE_UP = "\033[5~";
|
|
public const KEY_PAGE_DOWN = "\033[6~";
|
|
|
|
public function __construct(array $keySequence = [])
|
|
{
|
|
$this->keySequence = $keySequence;
|
|
}
|
|
|
|
/**
|
|
* Add a key press to the sequence
|
|
*/
|
|
public function pressKey(string $key): self
|
|
{
|
|
$this->keySequence[] = $key;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add multiple key presses
|
|
*/
|
|
public function pressKeys(array $keys): self
|
|
{
|
|
foreach ($keys as $key) {
|
|
$this->pressKey($key);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add arrow up key
|
|
*/
|
|
public function arrowUp(): self
|
|
{
|
|
return $this->pressKey(self::KEY_UP);
|
|
}
|
|
|
|
/**
|
|
* Add arrow down key
|
|
*/
|
|
public function arrowDown(): self
|
|
{
|
|
return $this->pressKey(self::KEY_DOWN);
|
|
}
|
|
|
|
/**
|
|
* Add arrow left key
|
|
*/
|
|
public function arrowLeft(): self
|
|
{
|
|
return $this->pressKey(self::KEY_LEFT);
|
|
}
|
|
|
|
/**
|
|
* Add arrow right key
|
|
*/
|
|
public function arrowRight(): self
|
|
{
|
|
return $this->pressKey(self::KEY_RIGHT);
|
|
}
|
|
|
|
/**
|
|
* Add enter key
|
|
*/
|
|
public function enter(): self
|
|
{
|
|
return $this->pressKey(self::KEY_ENTER);
|
|
}
|
|
|
|
/**
|
|
* Add escape key
|
|
*/
|
|
public function escape(): self
|
|
{
|
|
return $this->pressKey(self::KEY_ESCAPE);
|
|
}
|
|
|
|
/**
|
|
* Add tab key
|
|
*/
|
|
public function tab(): self
|
|
{
|
|
return $this->pressKey(self::KEY_TAB);
|
|
}
|
|
|
|
/**
|
|
* Get next key in sequence
|
|
*/
|
|
public function getNextKey(): ?string
|
|
{
|
|
if ($this->currentIndex >= count($this->keySequence)) {
|
|
return null;
|
|
}
|
|
|
|
$key = $this->keySequence[$this->currentIndex];
|
|
$this->currentIndex++;
|
|
|
|
return $key;
|
|
}
|
|
|
|
/**
|
|
* Reset to beginning
|
|
*/
|
|
public function reset(): void
|
|
{
|
|
$this->currentIndex = 0;
|
|
}
|
|
|
|
/**
|
|
* Check if there are more keys
|
|
*/
|
|
public function hasMoreKeys(): bool
|
|
{
|
|
return $this->currentIndex < count($this->keySequence);
|
|
}
|
|
|
|
/**
|
|
* Get all remaining keys
|
|
*/
|
|
public function getRemainingKeys(): array
|
|
{
|
|
return array_slice($this->keySequence, $this->currentIndex);
|
|
}
|
|
|
|
/**
|
|
* Get the full sequence
|
|
*/
|
|
public function getSequence(): array
|
|
{
|
|
return $this->keySequence;
|
|
}
|
|
}
|
|
|