-
Notifications
You must be signed in to change notification settings - Fork 108
Description
edit: This comment out of date. This is now a PR ticket. See comment #185 (comment)
Currently item rows look like
{marker} {text} {optional item extra}
The marker
property seems to only be toggable on highlighting a row. It is not aware of state, nor can it be changed once the menu is displayed.
My goal is to add checkbox-style visuals to this library.
With the following code, you can display a "checkbox" when an item was previously selected:
$itemCallable = function (CliMenu $menu) {
$item = $menu->getSelectedItem();
$key = array_search($item, $this->selected);
if ($key !== false) {
$item->hideItemExtra();
unset($this->selected[$key]);
} else {
$item->showItemExtra();
$this->selected[] = $item;
}
$menu->redraw();
};
$builder = (new CliMenuBuilder)
->setBackgroundColour('black')
->setForegroundColour('green');
$menu = $builder->setTitle('Create a New Project')
->addLineBreak()
->addStaticItem('Select Language')
->addItem('PHP', $itemCallable, true)
->addItem('Javascript', $itemCallable, true)
->addItem('Ruby', $itemCallable, true)
->addItem('Python', $itemCallable, true)
->addItem('Go', $itemCallable, true)
->addLineBreak('-')
->build();
// See https://github.com/php-school/cli-menu/issues/184
foreach ($menu->getItems() as $item) {
$item->hideItemExtra();
}
$menu->open();
foreach ($this->selected as $item) {
var_dump($item->getText());
}
This produces the following:
Unfortunately the item-extra string is pushed to the far right of the element. For one or two elements it might be fine, but for longer lists this could get a little confusing.
Instead, making it so the item-extra is to the left of the string, and hiding the marker makes it a nicer experience:
This is achieved by modifying getRows()
: https://github.com/php-school/cli-menu/blob/master/src/MenuItem/SelectableTrait.php#L31
public function getRows(MenuStyle $style, bool $selected = false) : array
{
$marker = sprintf("%s", $style->getMarker($selected));
$length = $style->getDisplaysExtra()
? $style->getContentWidth() - (mb_strlen($style->getItemExtra()) + 2)
: $style->getContentWidth();
$rows = explode(
"\n",
StringUtil::wordwrap(
sprintf('%s', $this->text),
$length,
sprintf("\n%s", str_repeat(' ', mb_strlen($marker)))
)
);
return array_map(function ($row, $key) use ($style, $length) {
$text = $this->disabled ? $style->getDisabledItemText($row) : $row;
if ($key === 0) {
return $this->showItemExtra
? sprintf('%s %s',
$style->getItemExtra(),
$text
)
: sprintf('%s %s',
str_repeat(' ', mb_strlen($style->getItemExtra())),
$text
);
}
return $text;
}, $rows, array_keys($rows));
}
I think it would be better to show a different item-extra when not selected:
[ ] Item not selected
[β] Item selected
From a technical standpoint, the marker
item would make more sense to refactor, giving it state-awareness (am I toggled even if I'm not highlighted?), giving it a different value for "toggled but not highlighted", and a value for "toggled and highlighted".
However, it seems the marker is at a much more static state than item-extra, and thus adding ability to pass in a custom parser for getRows()
to use makes more sense.
Since getRows()
is not mutating data but returning a new array, all current properties can remain protected
. Simply creating an interface for a parser to receive data, and return expected data would work.
Your thoughts?