diff --git a/src/CliMenu.php b/src/CliMenu.php index d5bd186a..0f02d346 100644 --- a/src/CliMenu.php +++ b/src/CliMenu.php @@ -353,23 +353,22 @@ protected function drawMenuItem(MenuItemInterface $item, bool $selected = false) { $rows = $item->getRows($this->style, $selected); - $setColour = $selected - ? $this->style->getSelectedSetCode() - : $this->style->getUnselectedSetCode(); + $setColour = $this->style->getColoursSetCode(); + $resetColour = $this->style->getColoursResetCode(); + $invertedColour = $selected + ? $this->style->getInvertedColoursSetCode() + : ''; - $unsetColour = $selected - ? $this->style->getSelectedUnsetCode() - : $this->style->getUnselectedUnsetCode(); - - return array_map(function ($row) use ($setColour, $unsetColour) { + return array_map(function ($row) use ($setColour, $invertedColour, $resetColour) { return sprintf( - "%s%s%s%s%s%s%s\n", + "%s%s%s%s%s%s%s%s\n", str_repeat(' ', $this->style->getMargin()), $setColour, + $invertedColour, str_repeat(' ', $this->style->getPadding()), $row, str_repeat(' ', $this->style->getRightHandPadding(mb_strlen(s::stripAnsiEscapeSequence($row)))), - $unsetColour, + $resetColour, str_repeat(' ', $this->style->getMargin()) ); }, $rows); diff --git a/src/CliMenuBuilder.php b/src/CliMenuBuilder.php index 8bcc742d..07ef41be 100644 --- a/src/CliMenuBuilder.php +++ b/src/CliMenuBuilder.php @@ -11,6 +11,7 @@ use PhpSchool\CliMenu\MenuItem\SelectableItem; use PhpSchool\CliMenu\MenuItem\StaticItem; use PhpSchool\CliMenu\Terminal\TerminalFactory; +use PhpSchool\CliMenu\Util\ColourUtil; use Assert\Assertion; use PhpSchool\Terminal\Terminal; use RuntimeException; @@ -198,20 +199,24 @@ public function setExitButtonText(string $exitButtonText) : self return $this; } - public function setBackgroundColour(string $colour) : self + public function setBackgroundColour($colour, string $fallback = null) : self { - Assertion::inArray($colour, MenuStyle::getAvailableColours()); - - $this->style['bg'] = $colour; + $this->style['bg'] = ColourUtil::validateColour( + $this->terminal, + $colour, + $fallback + ); return $this; } - public function setForegroundColour(string $colour) : self + public function setForegroundColour($colour, string $fallback = null) : self { - Assertion::inArray($colour, MenuStyle::getAvailableColours()); - - $this->style['fg'] = $colour; + $this->style['fg'] = ColourUtil::validateColour( + $this->terminal, + $colour, + $fallback + ); return $this; } diff --git a/src/Dialogue/Confirm.php b/src/Dialogue/Confirm.php index 68ee3f49..98ffb2fb 100644 --- a/src/Dialogue/Confirm.php +++ b/src/Dialogue/Confirm.php @@ -26,11 +26,11 @@ public function display(string $confirmText = 'OK') : void $this->write(sprintf( "%s%s%s%s%s\n", - $this->style->getUnselectedSetCode(), + $this->style->getColoursSetCode(), str_repeat(' ', $this->style->getPadding()), $this->text, str_repeat(' ', $this->style->getPadding()), - $this->style->getUnselectedUnsetCode() + $this->style->getColoursResetCode() )); $this->emptyRow(); @@ -39,25 +39,23 @@ public function display(string $confirmText = 'OK') : void $leftFill = ($promptWidth / 2) - (mb_strlen($confirmText) / 2); $this->write(sprintf( - "%s%s%s%s%s%s%s%s%s\n", - $this->style->getUnselectedSetCode(), + "%s%s%s%s%s%s%s\n", + $this->style->getColoursSetCode(), str_repeat(' ', $leftFill), - $this->style->getUnselectedUnsetCode(), - $this->style->getSelectedSetCode(), + $this->style->getInvertedColoursSetCode(), $confirmText, - $this->style->getSelectedUnsetCode(), - $this->style->getUnselectedSetCode(), + $this->style->getInvertedColoursUnsetCode(), str_repeat(' ', ceil($promptWidth - $leftFill - mb_strlen($confirmText))), - $this->style->getUnselectedUnsetCode() + $this->style->getColoursResetCode() )); $this->write(sprintf( "%s%s%s%s%s\n", - $this->style->getUnselectedSetCode(), + $this->style->getColoursSetCode(), str_repeat(' ', $this->style->getPadding()), str_repeat(' ', mb_strlen($this->text)), str_repeat(' ', $this->style->getPadding()), - $this->style->getUnselectedUnsetCode() + $this->style->getColoursResetCode() )); $this->terminal->moveCursorToTop(); diff --git a/src/Dialogue/Dialogue.php b/src/Dialogue/Dialogue.php index aa0e343a..91ec4de2 100644 --- a/src/Dialogue/Dialogue.php +++ b/src/Dialogue/Dialogue.php @@ -86,11 +86,11 @@ protected function emptyRow() : void $this->write( sprintf( "%s%s%s%s%s\n", - $this->style->getUnselectedSetCode(), + $this->style->getColoursSetCode(), str_repeat(' ', $this->style->getPadding()), str_repeat(' ', mb_strlen($this->text)), str_repeat(' ', $this->style->getPadding()), - $this->style->getUnselectedUnsetCode() + $this->style->getColoursResetCode() ) ); } diff --git a/src/Dialogue/Flash.php b/src/Dialogue/Flash.php index 2d305da5..b38ef0ba 100644 --- a/src/Dialogue/Flash.php +++ b/src/Dialogue/Flash.php @@ -23,11 +23,11 @@ public function display() : void $this->write(sprintf( "%s%s%s%s%s\n", - $this->style->getUnselectedSetCode(), + $this->style->getColoursSetCode(), str_repeat(' ', $this->style->getPadding()), $this->text, str_repeat(' ', $this->style->getPadding()), - $this->style->getUnselectedUnsetCode() + $this->style->getColoursResetCode() )); $this->emptyRow(); diff --git a/src/Input/InputIO.php b/src/Input/InputIO.php index d770c71d..739a8174 100644 --- a/src/Input/InputIO.php +++ b/src/Input/InputIO.php @@ -143,11 +143,11 @@ private function drawLine(Input $input, string $userInput, string $text) : void $line = sprintf( "%s%s%s%s%s\n", - $input->getStyle()->getUnselectedSetCode(), + $input->getStyle()->getColoursSetCode(), str_repeat(' ', $input->getStyle()->getPadding()), $text, str_repeat(' ', $input->getStyle()->getPadding()), - $input->getStyle()->getUnselectedUnsetCode() + $input->getStyle()->getColoursResetCode() ); $this->terminal->write($line); @@ -240,12 +240,10 @@ private function drawInputField(Input $input, string $userInput) : void $input, $userInput, sprintf( - '%s%s%s%s%s', - $input->getStyle()->getUnselectedUnsetCode(), - $input->getStyle()->getSelectedSetCode(), + '%s%s%s', + $input->getStyle()->getInvertedColoursSetCode(), $userInput, - $input->getStyle()->getSelectedUnsetCode(), - $input->getStyle()->getUnselectedSetCode() + $input->getStyle()->getInvertedColoursUnsetCode() ) ); } diff --git a/src/MenuStyle.php b/src/MenuStyle.php index c0a9493f..b59f4db0 100644 --- a/src/MenuStyle.php +++ b/src/MenuStyle.php @@ -4,6 +4,7 @@ use PhpSchool\CliMenu\Exception\InvalidInstantiationException; use PhpSchool\CliMenu\Terminal\TerminalFactory; +use PhpSchool\CliMenu\Util\ColourUtil; use PhpSchool\Terminal\Terminal; //TODO: B/W fallback @@ -19,12 +20,12 @@ class MenuStyle protected $terminal; /** - * @var string + * @var int|string */ protected $fg; /** - * @var string + * @var int|string */ protected $bg; @@ -73,6 +74,26 @@ class MenuStyle */ private $titleSeparator; + /** + * @var string + */ + private $coloursSetCode; + + /** + * @var string + */ + private $invertedColoursSetCode = "\033[7m"; + + /** + * @var string + */ + private $invertedColoursUnsetCode = "\033[27m"; + + /** + * @var string + */ + private $coloursResetCode = "\033[0m"; + /** * @var bool */ @@ -106,30 +127,30 @@ public static function getDefaultStyleValues() : array * @var array */ private static $availableForegroundColors = array( - 'black' => array('set' => 30, 'unset' => 39), - 'red' => array('set' => 31, 'unset' => 39), - 'green' => array('set' => 32, 'unset' => 39), - 'yellow' => array('set' => 33, 'unset' => 39), - 'blue' => array('set' => 34, 'unset' => 39), - 'magenta' => array('set' => 35, 'unset' => 39), - 'cyan' => array('set' => 36, 'unset' => 39), - 'white' => array('set' => 37, 'unset' => 39), - 'default' => array('set' => 39, 'unset' => 39), + 'black' => 30, + 'red' => 31, + 'green' => 32, + 'yellow' => 33, + 'blue' => 34, + 'magenta' => 35, + 'cyan' => 36, + 'white' => 37, + 'default' => 39, ); /** * @var array */ private static $availableBackgroundColors = array( - 'black' => array('set' => 40, 'unset' => 49), - 'red' => array('set' => 41, 'unset' => 49), - 'green' => array('set' => 42, 'unset' => 49), - 'yellow' => array('set' => 43, 'unset' => 49), - 'blue' => array('set' => 44, 'unset' => 49), - 'magenta' => array('set' => 45, 'unset' => 49), - 'cyan' => array('set' => 46, 'unset' => 49), - 'white' => array('set' => 47, 'unset' => 49), - 'default' => array('set' => 49, 'unset' => 49), + 'black' => 40, + 'red' => 41, + 'green' => 42, + 'yellow' => 43, + 'blue' => 44, + 'magenta' => 45, + 'cyan' => 46, + 'white' => 47, + 'default' => 49, ); /** @@ -163,11 +184,6 @@ public function __construct(Terminal $terminal = null) $this->setTitleSeparator(static::$defaultStyleValues['titleSeparator']); } - public static function getAvailableColours() : array - { - return array_keys(self::$availableBackgroundColors); - } - public function getDisabledItemText(string $text) : string { return sprintf( @@ -177,61 +193,57 @@ public function getDisabledItemText(string $text) : string self::$availableOptions['dim']['unset'] ); } - + /** - * Get the colour code set for Bg and Fg + * Generates the ansi escape sequence to set the colours */ - public function getSelectedSetCode() : string + private function generateColoursSetCode() : void { - return sprintf( - "\033[%sm", - implode(';', [ - self::$availableBackgroundColors[$this->getFg()]['set'], - self::$availableForegroundColors[$this->getBg()]['set'], - ]) - ); + if (is_string($this->fg)) { + $fgCode = self::$availableForegroundColors[$this->fg]; + } else { + $fgCode = sprintf("38;5;%s", $this->fg); + } + + if (is_string($this->bg)) { + $bgCode = self::$availableBackgroundColors[$this->bg]; + } else { + $bgCode = sprintf("48;5;%s", $this->bg); + } + + $this->coloursSetCode = sprintf("\033[%s;%sm", $fgCode, $bgCode); } /** - * Get the colour unset code for Bg and Fg + * Get the colour code for Bg and Fg */ - public function getSelectedUnsetCode() : string + public function getColoursSetCode() : string { - return sprintf( - "\033[%sm", - implode(';', [ - self::$availableBackgroundColors[$this->getBg()]['unset'], - self::$availableForegroundColors[$this->getFg()]['unset'], - ]) - ); + return $this->coloursSetCode; } /** - * Get the inverted colour code + * Get the inverted escape sequence (used for selected elements) */ - public function getUnselectedSetCode() : string + public function getInvertedColoursSetCode() : string { - return sprintf( - "\033[%sm", - implode(';', [ - self::$availableBackgroundColors[$this->getBg()]['set'], - self::$availableForegroundColors[$this->getFg()]['set'], - ]) - ); + return $this->invertedColoursSetCode; } /** - * Get the inverted colour unset code + * Get the inverted escape sequence (used for selected elements) */ - public function getUnselectedUnsetCode() : string + public function getInvertedColoursUnsetCode() : string { - return sprintf( - "\033[%sm", - implode(';', [ - self::$availableBackgroundColors[$this->getBg()]['unset'], - self::$availableForegroundColors[$this->getFg()]['unset'], - ]) - ); + return $this->invertedColoursUnsetCode; + } + + /** + * Get the escape sequence used to reset colours to default + */ + public function getColoursResetCode() : string + { + return $this->coloursResetCode; } /** @@ -242,26 +254,36 @@ protected function calculateContentWidth() : void $this->contentWidth = $this->width - ($this->padding * 2); } - public function getFg() : string + public function getFg() { return $this->fg; } - public function setFg(string $fg) : self + public function setFg($fg, string $fallback = null) : self { - $this->fg = $fg; + $this->fg = ColourUtil::validateColour( + $this->terminal, + $fg, + $fallback + ); + $this->generateColoursSetCode(); return $this; } - public function getBg() : string + public function getBg() { return $this->bg; } - public function setBg(string $bg) : self + public function setBg($bg, string $fallback = null) : self { - $this->bg = $bg; + $this->bg = ColourUtil::validateColour( + $this->terminal, + $bg, + $fallback + ); + $this->generateColoursSetCode(); return $this; } diff --git a/src/Util/ColourUtil.php b/src/Util/ColourUtil.php new file mode 100644 index 00000000..67a38b45 --- /dev/null +++ b/src/Util/ColourUtil.php @@ -0,0 +1,327 @@ + 'black', + 1 => 'red', + 2 => 'green', + 3 => 'yellow', + 4 => 'blue', + 5 => 'magenta', + 6 => 'cyan', + 7 => 'white', + 8 => 'black', + 9 => 'red', + 10 => 'green', + 11 => 'yellow', + 12 => 'blue', + 13 => 'magenta', + 14 => 'cyan', + 15 => 'white', + 16 => 'black', + 17 => 'blue', + 18 => 'blue', + 19 => 'blue', + 20 => 'blue', + 21 => 'blue', + 22 => 'green', + 23 => 'cyan', + 24 => 'cyan', + 25 => 'blue', + 26 => 'blue', + 27 => 'blue', + 28 => 'green', + 29 => 'green', + 30 => 'cyan', + 31 => 'cyan', + 32 => 'blue', + 33 => 'blue', + 34 => 'green', + 35 => 'green', + 36 => 'green', + 37 => 'cyan', + 38 => 'cyan', + 39 => 'cyan', + 40 => 'green', + 41 => 'green', + 42 => 'green', + 43 => 'cyan', + 44 => 'cyan', + 45 => 'cyan', + 46 => 'green', + 47 => 'green', + 48 => 'green', + 49 => 'green', + 50 => 'cyan', + 51 => 'cyan', + 52 => 'red', + 53 => 'blue', + 54 => 'blue', + 55 => 'blue', + 56 => 'blue', + 57 => 'blue', + 58 => 'yellow', + 59 => 'black', + 60 => 'blue', + 61 => 'blue', + 62 => 'blue', + 63 => 'blue', + 64 => 'green', + 65 => 'green', + 66 => 'cyan', + 67 => 'cyan', + 68 => 'blue', + 69 => 'blue', + 70 => 'green', + 71 => 'green', + 72 => 'green', + 73 => 'cyan', + 74 => 'cyan', + 75 => 'cyan', + 76 => 'green', + 77 => 'green', + 78 => 'green', + 79 => 'green', + 80 => 'cyan', + 81 => 'cyan', + 82 => 'green', + 83 => 'green', + 84 => 'green', + 85 => 'green', + 86 => 'cyan', + 87 => 'cyan', + 88 => 'red', + 89 => 'magenta', + 90 => 'magenta', + 91 => 'magenta', + 92 => 'blue', + 93 => 'blue', + 94 => 'yellow', + 95 => 'red', + 96 => 'magenta', + 97 => 'magenta', + 98 => 'blue', + 99 => 'blue', + 100 => 'yellow', + 101 => 'yellow', + 102 => 'white', + 103 => 'blue', + 104 => 'blue', + 105 => 'blue', + 106 => 'green', + 107 => 'green', + 108 => 'green', + 109 => 'cyan', + 110 => 'cyan', + 111 => 'cyan', + 112 => 'green', + 113 => 'green', + 114 => 'green', + 115 => 'green', + 116 => 'cyan', + 117 => 'cyan', + 118 => 'green', + 119 => 'green', + 120 => 'green', + 121 => 'green', + 122 => 'green', + 123 => 'cyan', + 124 => 'red', + 125 => 'magenta', + 126 => 'magenta', + 127 => 'magenta', + 128 => 'magenta', + 129 => 'magenta', + 130 => 'red', + 131 => 'red', + 132 => 'magenta', + 133 => 'magenta', + 134 => 'magenta', + 135 => 'magenta', + 136 => 'yellow', + 137 => 'red', + 138 => 'red', + 139 => 'magenta', + 140 => 'magenta', + 141 => 'magenta', + 142 => 'yellow', + 143 => 'yellow', + 144 => 'yellow', + 145 => 'white', + 146 => 'white', + 147 => 'white', + 148 => 'yellow', + 149 => 'green', + 150 => 'green', + 151 => 'green', + 152 => 'cyan', + 153 => 'white', + 154 => 'green', + 155 => 'green', + 156 => 'green', + 157 => 'green', + 158 => 'green', + 159 => 'cyan', + 160 => 'red', + 161 => 'magenta', + 162 => 'magenta', + 163 => 'magenta', + 164 => 'magenta', + 165 => 'magenta', + 166 => 'red', + 167 => 'red', + 168 => 'magenta', + 169 => 'magenta', + 170 => 'magenta', + 171 => 'magenta', + 172 => 'red', + 173 => 'red', + 174 => 'red', + 175 => 'magenta', + 176 => 'magenta', + 177 => 'magenta', + 178 => 'yellow', + 179 => 'yellow', + 180 => 'white', + 181 => 'white', + 182 => 'magenta', + 183 => 'magenta', + 184 => 'yellow', + 185 => 'yellow', + 186 => 'yellow', + 187 => 'yellow', + 188 => 'white', + 189 => 'white', + 190 => 'yellow', + 191 => 'yellow', + 192 => 'green', + 193 => 'green', + 194 => 'green', + 195 => 'cyan', + 196 => 'red', + 197 => 'red', + 198 => 'magenta', + 199 => 'magenta', + 200 => 'magenta', + 201 => 'magenta', + 202 => 'red', + 203 => 'red', + 204 => 'magenta', + 205 => 'magenta', + 206 => 'magenta', + 207 => 'magenta', + 208 => 'red', + 209 => 'red', + 210 => 'red', + 211 => 'magenta', + 212 => 'magenta', + 213 => 'magenta', + 214 => 'red', + 215 => 'white', + 216 => 'red', + 217 => 'red', + 218 => 'magenta', + 219 => 'magenta', + 220 => 'yellow', + 221 => 'yellow', + 222 => 'yellow', + 223 => 'white', + 224 => 'white', + 225 => 'magenta', + 226 => 'yellow', + 227 => 'yellow', + 228 => 'yellow', + 229 => 'yellow', + 230 => 'yellow', + 231 => 'white', + 232 => 'black', + 233 => 'black', + 234 => 'black', + 235 => 'black', + 236 => 'black', + 237 => 'black', + 238 => 'black', + 239 => 'black', + 240 => 'black', + 241 => 'black', + 242 => 'black', + 243 => 'black', + 244 => 'white', + 245 => 'white', + 246 => 'white', + 247 => 'white', + 248 => 'white', + 249 => 'white', + 250 => 'white', + 251 => 'white', + 252 => 'white', + 253 => 'white', + 254 => 'white', + 255 => 'white', + ]; + + public static function getDefaultColoursNames() : array + { + return static::$defaultColoursNames; + } + + /** + * Simple function to transform a 8-bit (256 colours) colour code + * to one of the default 8 colors available in the terminal + */ + public static function map256To8(int $colourCode) : string + { + if (!isset(static::$coloursMap[$colourCode])) { + throw new \InvalidArgumentException("Invalid colour code"); + } + + return static::$coloursMap[$colourCode]; + } + + /** + * Check if $colour exists + * If it's a 256-colours code and $terminal doesn't support it, returns a fallback value + */ + public static function validateColour(Terminal $terminal, $colour, string $fallback = null) + { + if (is_int($colour)) { + if ($colour < 0 || $colour > 255) { + throw new \InvalidArgumentException("Invalid colour code"); + } + if ($terminal->getColourSupport() < 256) { + if ($fallback !== null) { + Assertion::inArray($fallback, static::getDefaultColoursNames()); + return $fallback; + } + return static::map256To8($colour); + } + } else { + Assertion::inArray($colour, static::getDefaultColoursNames()); + } + return $colour; + } +} diff --git a/test/CliMenuBuilderTest.php b/test/CliMenuBuilderTest.php index 8cf6bc7d..72f05dbd 100644 --- a/test/CliMenuBuilderTest.php +++ b/test/CliMenuBuilderTest.php @@ -83,6 +83,71 @@ public function testModifyStyles() : void $this->checkStyleVariable($menu, 'titleSeparator', '-'); } + public function test256ColoursCodes() : void + { + $terminal = static::createMock(Terminal::class); + $terminal + ->expects($this->any()) + ->method('getColourSupport') + ->will($this->returnValue(256)); + + $builder = new CliMenuBuilder; + $builder->setTerminal($terminal); + $builder->setBackgroundColour(16, 'white'); + $builder->setForegroundColour(206, 'red'); + $menu = $builder->build(); + + $this->checkStyleVariable($menu, 'bg', 16); + $this->checkStyleVariable($menu, 'fg', 206); + + $terminal = static::createMock(Terminal::class); + $terminal + ->expects($this->any()) + ->method('getColourSupport') + ->will($this->returnValue(8)); + + $builder = new CliMenuBuilder; + $builder->setTerminal($terminal); + $builder->setBackgroundColour(16, 'white'); + $builder->setForegroundColour(206, 'red'); + $menu = $builder->build(); + + $this->checkStyleVariable($menu, 'bg', 'white'); + $this->checkStyleVariable($menu, 'fg', 'red'); + } + + public function testSetFgThrowsExceptionWhenColourCodeIsNotInRange() : void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid colour code'); + + $terminal = static::createMock(Terminal::class); + $terminal + ->expects($this->any()) + ->method('getColourSupport') + ->will($this->returnValue(256)); + + $builder = new CliMenuBuilder; + $builder->setTerminal($terminal); + $builder->setForegroundColour(512, 'white'); + } + + public function testSetBgThrowsExceptionWhenColourCodeIsNotInRange() : void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid colour code'); + + $terminal = static::createMock(Terminal::class); + $terminal + ->expects($this->any()) + ->method('getColourSupport') + ->will($this->returnValue(256)); + + $builder = new CliMenuBuilder; + $builder->setTerminal($terminal); + $builder->setBackgroundColour(-5, 'white'); + } + public function testDisableDefaultItems() : void { $builder = new CliMenuBuilder; diff --git a/test/MenuStyleTest.php b/test/MenuStyleTest.php index 407445b4..ca861b12 100644 --- a/test/MenuStyleTest.php +++ b/test/MenuStyleTest.php @@ -5,8 +5,9 @@ use PhpSchool\CliMenu\CliMenuBuilder; use PhpSchool\CliMenu\Exception\InvalidInstantiationException; use PhpSchool\CliMenu\MenuStyle; -use PhpSchool\CliMenu\Terminal\TerminalInterface; -use PhpSchool\CliMenu\Terminal\UnixTerminal; +use PhpSchool\CliMenu\Util\ColourUtil; +use PhpSchool\Terminal\Terminal; +use PhpSchool\Terminal\UnixTerminal; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -15,7 +16,7 @@ */ class MenuStyleTest extends TestCase { - private function getMenuStyle() : MenuStyle + private function getMenuStyle(int $colours = 8) : MenuStyle { // Use the CliMenuBuilder & reflection to get the style Obj $builder = new CliMenuBuilder(); @@ -29,7 +30,7 @@ private function getMenuStyle() : MenuStyle $reflectionStyle = new \ReflectionObject($style); $terminalProperty = $reflectionStyle->getProperty('terminal'); $terminalProperty->setAccessible(true); - $terminalProperty->setValue($style, $this->getMockTerminal()); + $terminalProperty->setValue($style, $this->getMockTerminal($colours)); // Force recalculate terminal widths now terminal is set $style->setWidth(100); @@ -37,12 +38,12 @@ private function getMenuStyle() : MenuStyle return $style; } - private function getMockTerminal() : MockObject + private function getMockTerminal(int $colours = 8) : MockObject { $terminal = $this ->getMockBuilder(UnixTerminal::class) ->disableOriginalConstructor() - ->setMethods(['getWidth']) + ->setMethods(['getWidth', 'getColourSupport']) ->getMock(); $terminal @@ -50,6 +51,11 @@ private function getMockTerminal() : MockObject ->method('getWidth') ->will(static::returnValue(500)); + $terminal + ->expects(static::any()) + ->method('getColourSupport') + ->will(static::returnValue($colours)); + return $terminal; } @@ -80,27 +86,27 @@ public function testAvailableColours() : void 'cyan', 'white', 'default' - ], MenuStyle::getAvailableColours()); + ], ColourUtil::getDefaultColoursNames()); } - public function testGetSelectedSetCode() : void + public function testGetColoursSetCode() : void { - static::assertSame("\e[47;34m", $this->getMenuStyle()->getSelectedSetCode()); + static::assertSame("\e[37;44m", $this->getMenuStyle()->getColoursSetCode()); } - public function testGetSelectedUnsetCode() : void + public function testGetColoursResetCode() : void { - static::assertSame("\e[49;39m", $this->getMenuStyle()->getSelectedUnsetCode()); + static::assertSame("\e[0m", $this->getMenuStyle()->getColoursResetCode()); } - public function testGetUnselectedSetCode() : void + public function testGetInvertedColoursSetCode() : void { - static::assertSame("\e[44;37m", $this->getMenuStyle()->getUnselectedSetCode()); + static::assertSame("\e[7m", $this->getMenuStyle()->getInvertedColoursSetCode()); } - public function testGetUnselectedUnsetCode() : void + public function testGetInvertedColoursUnsetCode() : void { - static::assertSame("\e[49;39m", $this->getMenuStyle()->getUnselectedUnsetCode()); + static::assertSame("\e[27m", $this->getMenuStyle()->getInvertedColoursUnsetCode()); } public function testGetterAndSetters() : void @@ -141,6 +147,41 @@ public function testGetterAndSetters() : void static::assertSame(10, $style->getPadding()); } + public function test256ColoursCodes() : void + { + $style = $this->getMenuStyle(256); + $style->setBg(16, 'white'); + $style->setFg(206, 'red'); + static::assertSame(16, $style->getBg()); + static::assertSame(206, $style->getFg()); + static::assertSame("\033[38;5;206;48;5;16m", $style->getColoursSetCode()); + + $style = $this->getMenuStyle(8); + $style->setBg(16, 'white'); + $style->setFg(206, 'red'); + static::assertSame('white', $style->getBg()); + static::assertSame('red', $style->getFg()); + static::assertSame("\033[31;47m", $style->getColoursSetCode()); + } + + public function testSetFgThrowsExceptionWhenColourCodeIsNotInRange() : void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid colour code'); + + $style = $this->getMenuStyle(256); + $style->setFg(512, 'white'); + } + + public function testSetBgThrowsExceptionWhenColourCodeIsNotInRange() : void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid colour code'); + + $style = $this->getMenuStyle(256); + $style->setBg(-5, 'white'); + } + public function testGetMarkerReturnsTheCorrectMarkers() : void { $style = $this->getMenuStyle();