2024-10-01 22:31:51 +02:00
|
|
|
<?php
|
|
|
|
|
|
2024-10-02 01:15:22 +02:00
|
|
|
namespace NoccyLabs\JsonEdit\Editor;
|
2024-10-01 22:31:51 +02:00
|
|
|
|
2024-10-02 01:15:22 +02:00
|
|
|
use NoccyLabs\JsonEdit\Terminal\Terminal;
|
2024-10-01 22:31:51 +02:00
|
|
|
|
|
|
|
|
class Menu
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
const U_CORNER_TOPLEFT = "\u{250c}";
|
|
|
|
|
const U_CORNER_TOPRIGHT = "\u{2510}";
|
|
|
|
|
const U_CORNER_BOTTOMLEFT = "\u{2514}";
|
|
|
|
|
const U_CORNER_BOTTOMRIGHT = "\u{2518}";
|
|
|
|
|
|
|
|
|
|
const U_CORNER_ROUNDED_TOPLEFT = "\u{256d}";
|
|
|
|
|
const U_CORNER_ROUNDED_TOPRIGHT = "\u{256e}";
|
|
|
|
|
const U_CORNER_ROUNDED_BOTTOMLEFT = "\u{2570}";
|
|
|
|
|
const U_CORNER_ROUNDED_BOTTOMRIGHT = "\u{256f}";
|
|
|
|
|
|
|
|
|
|
const U_EDGE_HORIZONTAL = "\u{2500}";
|
|
|
|
|
|
|
|
|
|
const U_EDGE_VERTICAL = "\u{2502}";
|
|
|
|
|
const U_EDGE_VERTICAL_SCROLL = "\u{250a}";
|
|
|
|
|
const U_EDGE_VERTICAL_THUMB = "\u{2503}";
|
|
|
|
|
const U_EDGE_VERTICAL_LEFT = "\u{2524}";
|
|
|
|
|
const U_EDGE_VERTICAL_RIGHT = "\u{251c}";
|
|
|
|
|
|
|
|
|
|
private int $index = 0;
|
|
|
|
|
|
2024-10-03 01:44:10 +02:00
|
|
|
private int $scroll = 0;
|
|
|
|
|
|
2024-10-01 22:31:51 +02:00
|
|
|
public function __construct(private Terminal $terminal, private array $items, private string $title = 'Menu')
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function display(int $left, int $top, int $width, int $height = 0, string|int|null $value): mixed
|
|
|
|
|
{
|
|
|
|
|
|
2024-10-03 01:44:10 +02:00
|
|
|
[$w,$h] = $this->terminal->getSize();
|
|
|
|
|
if ($height == 0) {
|
2024-10-03 15:29:56 +02:00
|
|
|
$height = min($h - 5, count($this->items) + 2);
|
2024-10-03 01:44:10 +02:00
|
|
|
}
|
|
|
|
|
if ($left == 0) {
|
|
|
|
|
$left = round(($w - $width) / 2);
|
|
|
|
|
}
|
|
|
|
|
if ($top == 0) {
|
|
|
|
|
$top = round(($h - $height) / 2);
|
|
|
|
|
}
|
2024-10-01 22:31:51 +02:00
|
|
|
|
|
|
|
|
$this->redraw($left, $top, $width, $height);
|
|
|
|
|
|
|
|
|
|
$showing = true;
|
|
|
|
|
while ($showing) {
|
|
|
|
|
while (null === ($ch = $this->terminal->readKey())) {
|
|
|
|
|
usleep(10000);
|
|
|
|
|
}
|
|
|
|
|
if (mb_strlen($ch) == 1) {
|
|
|
|
|
if ($ch == "\x03") {
|
|
|
|
|
$showing = false;
|
|
|
|
|
}
|
|
|
|
|
if ($ch == "\r") {
|
|
|
|
|
return array_keys($this->items)[$this->index];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ($ch == "k{UP}") {
|
|
|
|
|
$this->index--;
|
|
|
|
|
if ($this->index < 0) $this->index = count($this->items) - 1;
|
|
|
|
|
$this->redraw($left, $top, $width, $height);
|
|
|
|
|
} elseif ($ch == "k{DOWN}") {
|
|
|
|
|
$this->index++;
|
|
|
|
|
if ($this->index >= count($this->items)) $this->index = 0;
|
|
|
|
|
$this->redraw($left, $top, $width, $height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function redraw(int $left, int $top, int $width, int $height) {
|
2024-10-03 01:44:10 +02:00
|
|
|
|
2024-10-03 01:58:50 +02:00
|
|
|
$visibleItems = $height - 2;
|
2024-10-03 01:44:10 +02:00
|
|
|
|
|
|
|
|
$scrollTop = $this->scroll;
|
|
|
|
|
$scrollVisible = $visibleItems / count($this->items); // / $visibleItems;
|
|
|
|
|
$scrollBottom = $scrollTop + $scrollVisible;
|
|
|
|
|
$thumbTop = round($scrollTop * $visibleItems);
|
|
|
|
|
$thumbBottom = round($visibleItems * $scrollBottom);
|
|
|
|
|
|
2024-10-01 22:31:51 +02:00
|
|
|
// draw head
|
|
|
|
|
echo "\e[40;37m";
|
|
|
|
|
$this->terminal
|
|
|
|
|
->writeAt($left, $top + 0, self::U_CORNER_ROUNDED_TOPLEFT.str_repeat(self::U_EDGE_HORIZONTAL,$width - 2).self::U_CORNER_ROUNDED_TOPRIGHT)
|
2024-10-03 01:58:50 +02:00
|
|
|
//->writeAt($left, $top + 1, self::U_EDGE_VERTICAL.str_repeat(" ",$width - 2).self::U_EDGE_VERTICAL)
|
|
|
|
|
//->writeAt($left + 2, $top + 1, "\e[1m" . $this->title . "\e[22m")
|
|
|
|
|
//->writeAt($left, $top + 2, self::U_EDGE_VERTICAL_RIGHT.str_repeat(self::U_EDGE_HORIZONTAL,$width - 2).self::U_EDGE_VERTICAL_LEFT)
|
2024-10-01 22:31:51 +02:00
|
|
|
->writeAt($left, $top + $height - 1, self::U_CORNER_BOTTOMLEFT.str_repeat(self::U_EDGE_HORIZONTAL,$width - 2).self::U_CORNER_BOTTOMRIGHT)
|
|
|
|
|
;
|
|
|
|
|
$keys = array_keys($this->items);
|
|
|
|
|
for ($n = 0; $n < $visibleItems; $n++) {
|
|
|
|
|
$key = $keys[$n]??null;
|
2024-10-03 01:58:50 +02:00
|
|
|
$item = ($key ? ($this->items[$key]) : null);
|
|
|
|
|
if ($item === "---") {
|
|
|
|
|
$item = "\e[2m".str_repeat("\u{2500}", $width - 2)."\e[22m";
|
|
|
|
|
} elseif (is_null($item)) {
|
|
|
|
|
$item = str_repeat(" ", $width - 2);
|
|
|
|
|
} else {
|
|
|
|
|
$item = " " . $item . str_repeat(" ", $width - 3 - $this->itemlen($item)) . "\e[40;37m";
|
|
|
|
|
$item = (($n == $this->index)?"\e[37;44m":"\e[40;37m") . $item;
|
|
|
|
|
}
|
2024-10-03 01:44:10 +02:00
|
|
|
if ($n >= $thumbTop && $n <= $thumbBottom) {
|
|
|
|
|
$scrollbar = "\e[97;1m".self::U_EDGE_VERTICAL_THUMB."\e[40;37;22m";
|
|
|
|
|
} else {
|
|
|
|
|
$scrollbar = "\e[37;2m".self::U_EDGE_VERTICAL_SCROLL."\e[40;37;22m";
|
|
|
|
|
}
|
2024-10-01 22:31:51 +02:00
|
|
|
$this->terminal
|
2024-10-03 01:58:50 +02:00
|
|
|
->writeAt($left, $top + 1 + $n, self::U_EDGE_VERTICAL.$item.$scrollbar);
|
2024-10-01 22:31:51 +02:00
|
|
|
}
|
|
|
|
|
echo "\e[0m";
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-03 01:44:10 +02:00
|
|
|
private function itemlen(string $item): int
|
|
|
|
|
{
|
|
|
|
|
$item = preg_replace('<\e\[.+?m>', '', $item);
|
|
|
|
|
return mb_strlen($item);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-01 22:31:51 +02:00
|
|
|
}
|