Skip to content

Commit 12607bf

Browse files
committed
Initial toggleable item support
1 parent d80c749 commit 12607bf

File tree

4 files changed

+274
-16
lines changed

4 files changed

+274
-16
lines changed

examples/toggleable-item.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
use PhpSchool\CliMenu\CliMenu;
4+
use PhpSchool\CliMenu\Builder\CliMenuBuilder;
5+
use PhpSchool\CliMenu\MenuItem\ToggleableItem;
6+
7+
require_once(__DIR__ . '/../vendor/autoload.php');
8+
9+
$itemCallable = function (CliMenu $menu) {
10+
/** @var ToggleableItem $item */
11+
$item = $menu->getSelectedItem();
12+
13+
$item->setToggled(!$item->getToggled());
14+
15+
$menu->redraw();
16+
};
17+
18+
$menu = (new CliMenuBuilder)
19+
->setTitle('Select a Language')
20+
->addSubMenu('Compiled', function (CliMenuBuilder $b) use($itemCallable) {
21+
$b->setTitle('Compiled Languages')
22+
->addToggleableItem('Rust', $itemCallable)
23+
->addToggleableItem('C++', $itemCallable)
24+
->addToggleableItem('Go', $itemCallable)
25+
->addToggleableItem('Java', $itemCallable)
26+
->addToggleableItem('C', $itemCallable)
27+
;
28+
})
29+
->addSubMenu('Interpreted', function (CliMenuBuilder $b) use($itemCallable) {
30+
$b->setTitle('Interpreted Languages')
31+
->setUntoggledMarker('[ ]')
32+
->setToggledMarker('[✔]')
33+
->addToggleableItem('PHP', $itemCallable)
34+
->addToggleableItem('Javascript', $itemCallable)
35+
->addToggleableItem('Ruby', $itemCallable)
36+
->addToggleableItem('Python', $itemCallable)
37+
;
38+
})
39+
->build();
40+
41+
$menu->open();

src/Builder/CliMenuBuilder.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
1111
use PhpSchool\CliMenu\MenuItem\MenuMenuItem;
1212
use PhpSchool\CliMenu\MenuItem\SelectableItem;
13+
use PhpSchool\CliMenu\MenuItem\ToggleableItem;
1314
use PhpSchool\CliMenu\CliMenu;
1415
use PhpSchool\CliMenu\MenuItem\SplitItem;
1516
use PhpSchool\CliMenu\MenuItem\StaticItem;
@@ -130,6 +131,17 @@ public function addItems(array $items) : self
130131
return $this;
131132
}
132133

134+
public function addToggleableItem(
135+
string $text,
136+
callable $itemCallable,
137+
bool $showItemExtra = false,
138+
bool $disabled = false
139+
) : self {
140+
$this->addMenuItem(new ToggleableItem($text, $itemCallable, $showItemExtra, $disabled));
141+
142+
return $this;
143+
}
144+
133145
public function addStaticItem(string $text) : self
134146
{
135147
$this->addMenuItem(new StaticItem($text));
@@ -395,6 +407,20 @@ public function setSelectedMarker(string $marker) : self
395407
return $this;
396408
}
397409

410+
public function setUntoggledMarker(string $marker) : self
411+
{
412+
$this->style->setUntoggledMarker($marker);
413+
414+
return $this;
415+
}
416+
417+
public function setToggledMarker(string $marker) : self
418+
{
419+
$this->style->setToggledMarker($marker);
420+
421+
return $this;
422+
}
423+
398424
public function setItemExtra(string $extra) : self
399425
{
400426
$this->style->setItemExtra($extra);

src/MenuItem/ToggleableItem.php

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php
2+
3+
namespace PhpSchool\CliMenu\MenuItem;
4+
5+
use PhpSchool\CliMenu\MenuItem;
6+
use PhpSchool\CliMenu\MenuStyle;
7+
use PhpSchool\CliMenu\Util\StringUtil;
8+
9+
class ToggleableItem implements MenuItem\MenuItemInterface
10+
{
11+
/**
12+
* @var callable
13+
*/
14+
private $selectAction;
15+
16+
/**
17+
* @var string
18+
*/
19+
private $text = '';
20+
21+
/**
22+
* @var bool
23+
*/
24+
private $showItemExtra = false;
25+
26+
/**
27+
* @var bool
28+
*/
29+
private $disabled = false;
30+
31+
/**
32+
* @var bool
33+
*/
34+
private $toggled = false;
35+
36+
public function __construct(
37+
string $text,
38+
callable $selectAction,
39+
bool $showItemExtra = false,
40+
bool $disabled = false
41+
) {
42+
$this->text = $text;
43+
$this->selectAction = $selectAction;
44+
$this->showItemExtra = $showItemExtra;
45+
$this->disabled = $disabled;
46+
}
47+
48+
/**
49+
* Execute the items callable if required
50+
*/
51+
public function getSelectAction() : ?callable
52+
{
53+
return $this->selectAction;
54+
}
55+
56+
/**
57+
* Return the raw string of text
58+
*/
59+
public function getText() : string
60+
{
61+
return $this->text;
62+
}
63+
64+
/**
65+
* Set the raw string of text
66+
*/
67+
public function setText(string $text) : void
68+
{
69+
$this->text = $text;
70+
}
71+
72+
/**
73+
* The output text for the item
74+
*/
75+
public function getRows(MenuStyle $style, bool $toggled = false) : array
76+
{
77+
$marker = sprintf("%s", $style->getMarkerToggled($this->toggled));
78+
79+
$length = $style->getDisplaysExtra()
80+
? $style->getContentWidth() - (mb_strlen($style->getItemExtra()) + 2)
81+
: $style->getContentWidth();
82+
83+
$rows = explode(
84+
"\n",
85+
StringUtil::wordwrap(
86+
sprintf('%s %s', $marker, $this->text),
87+
$length,
88+
sprintf("\n%s", str_repeat(' ', mb_strlen($marker)))
89+
)
90+
);
91+
92+
return array_map(function ($row, $key) use ($style, $length) {
93+
$text = $this->disabled ? $style->getDisabledItemText($row) : $row;
94+
95+
if ($key === 0) {
96+
return $this->showItemExtra
97+
? sprintf('%s%s %s', $text, str_repeat(' ', $length - mb_strlen($row)), $style->getItemExtra())
98+
: $text;
99+
}
100+
101+
return $text;
102+
}, $rows, array_keys($rows));
103+
}
104+
105+
/**
106+
* Can the item be selected
107+
*/
108+
public function canSelect() : bool
109+
{
110+
return !$this->disabled;
111+
}
112+
113+
public function showsItemExtra() : bool
114+
{
115+
return $this->showItemExtra;
116+
}
117+
118+
/**
119+
* Enable showing item extra
120+
*/
121+
public function showItemExtra() : void
122+
{
123+
$this->showItemExtra = true;
124+
}
125+
126+
/**
127+
* Disable showing item extra
128+
*/
129+
public function hideItemExtra() : void
130+
{
131+
$this->showItemExtra = false;
132+
}
133+
134+
public function setToggled(bool $toggled)
135+
{
136+
$this->toggled = $toggled;
137+
}
138+
139+
public function getToggled(): bool
140+
{
141+
return $this->toggled;
142+
}
143+
}

src/MenuStyle.php

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ class MenuStyle
6969
*/
7070
private $unselectedMarker;
7171

72+
/**
73+
* @var string
74+
*/
75+
private $toggledMarker;
76+
77+
/**
78+
* @var string
79+
*/
80+
private $untoggledMarker;
81+
7282
/**
7383
* @var string
7484
*/
@@ -150,23 +160,25 @@ class MenuStyle
150160
* @var array
151161
*/
152162
private static $defaultStyleValues = [
153-
'fg' => 'white',
154-
'bg' => 'blue',
155-
'width' => 100,
156-
'paddingTopBottom' => 1,
157-
'paddingLeftRight' => 2,
158-
'margin' => 2,
159-
'selectedMarker' => '',
160-
'unselectedMarker' => '',
161-
'itemExtra' => '',
162-
'displaysExtra' => false,
163-
'titleSeparator' => '=',
164-
'borderTopWidth' => 0,
165-
'borderRightWidth' => 0,
163+
'fg' => 'white',
164+
'bg' => 'blue',
165+
'width' => 100,
166+
'paddingTopBottom' => 1,
167+
'paddingLeftRight' => 2,
168+
'margin' => 2,
169+
'selectedMarker' => '',
170+
'unselectedMarker' => '',
171+
'toggledMarker' => '[●] ',
172+
'untoggledMarker' => '[○] ',
173+
'itemExtra' => '',
174+
'displaysExtra' => false,
175+
'titleSeparator' => '=',
176+
'borderTopWidth' => 0,
177+
'borderRightWidth' => 0,
166178
'borderBottomWidth' => 0,
167-
'borderLeftWidth' => 0,
168-
'borderColour' => 'white',
169-
'marginAuto' => false,
179+
'borderLeftWidth' => 0,
180+
'borderColour' => 'white',
181+
'marginAuto' => false,
170182
];
171183

172184
/**
@@ -229,6 +241,8 @@ public function __construct(Terminal $terminal = null)
229241
$this->setMargin(self::$defaultStyleValues['margin']);
230242
$this->setSelectedMarker(self::$defaultStyleValues['selectedMarker']);
231243
$this->setUnselectedMarker(self::$defaultStyleValues['unselectedMarker']);
244+
$this->setToggledMarker(self::$defaultStyleValues['toggledMarker']);
245+
$this->setUntoggledMarker(self::$defaultStyleValues['untoggledMarker']);
232246
$this->setItemExtra(self::$defaultStyleValues['itemExtra']);
233247
$this->setDisplaysExtra(self::$defaultStyleValues['displaysExtra']);
234248
$this->setTitleSeparator(self::$defaultStyleValues['titleSeparator']);
@@ -250,6 +264,8 @@ public function hasChangedFromDefaults() : bool
250264
$this->margin,
251265
$this->selectedMarker,
252266
$this->unselectedMarker,
267+
$this->toggledMarker,
268+
$this->untoggledMarker,
253269
$this->itemExtra,
254270
$this->displaysExtra,
255271
$this->titleSeparator,
@@ -557,6 +573,38 @@ public function getMarker(bool $selected) : string
557573
return $selected ? $this->selectedMarker : $this->unselectedMarker;
558574
}
559575

576+
public function getToggledMarker() : string
577+
{
578+
return $this->toggledMarker;
579+
}
580+
581+
public function setToggledMarker(string $marker) : self
582+
{
583+
$this->toggledMarker = $marker;
584+
585+
return $this;
586+
}
587+
588+
public function getUntoggledMarker() : string
589+
{
590+
return $this->untoggledMarker;
591+
}
592+
593+
public function setUntoggledMarker(string $marker) : self
594+
{
595+
$this->untoggledMarker = $marker;
596+
597+
return $this;
598+
}
599+
600+
/**
601+
* Get the correct toggled marker for the item
602+
*/
603+
public function getMarkerToggled(bool $toggled) : string
604+
{
605+
return $toggled ? $this->toggledMarker : $this->untoggledMarker;
606+
}
607+
560608
public function setItemExtra(string $itemExtra) : self
561609
{
562610
$this->itemExtra = $itemExtra;

0 commit comments

Comments
 (0)