Skip to content

Commit 4992019

Browse files
authored
Merge pull request #214 from jtreminio/feature/selectable-style
Selectable item styles
2 parents 389ef37 + 09b4294 commit 4992019

File tree

10 files changed

+384
-46
lines changed

10 files changed

+384
-46
lines changed

README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -801,13 +801,17 @@ markers only display on *selectable* items, which are: `\PhpSchool\CliMenu\MenuI
801801
<?php
802802

803803
use PhpSchool\CliMenu\Builder\CliMenuBuilder;
804+
use PhpSchool\CliMenu\Style\SelectableStyle;
804805

805806
$menu = (new CliMenuBuilder)
806-
->setUnselectedMarker('❅ ')
807-
->setSelectedMarker('✏ ')
808-
809-
//disable unselected marker
810-
->setUnselectedMarker('')
807+
->modifySelectableStyle(function (SelectableStyle $style) {
808+
$style->setUnselectedMarker('❅ ')
809+
->setSelectedMarker('✏ ')
810+
811+
// disable unselected marker
812+
->setUnselectedMarker('')
813+
;
814+
})
811815
->build();
812816
```
813817

@@ -858,9 +862,12 @@ The third parameter to `addItem` is a boolean whether to show the item extra or
858862

859863
use PhpSchool\CliMenu\Builder\CliMenuBuilder;
860864
use PhpSchool\CliMenu\CliMenu;
865+
use PhpSchool\CliMenu\Style\SelectableStyle;
861866

862867
$menu = (new CliMenuBuilder)
863-
->setItemExtra('✔')
868+
->modifySelectableStyle(function (SelectableStyle $style) {
869+
$style->setItemExtra('✔');
870+
})
864871
->addItem('Exercise 1', function (CliMenu $menu) { echo 'I am complete!'; }, true)
865872
->build();
866873
```

examples/custom-styles.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use PhpSchool\CliMenu\CliMenu;
44
use PhpSchool\CliMenu\Builder\CliMenuBuilder;
5+
use PhpSchool\CliMenu\Style\SelectableStyle;
56

67
require_once(__DIR__ . '/../vendor/autoload.php');
78

@@ -20,9 +21,11 @@
2021
->setPadding(4)
2122
->setMargin(4)
2223
->setBorder(1, 2, 'red')
23-
->setUnselectedMarker(' ')
24-
->setSelectedMarker('>')
2524
->setTitleSeparator('- ')
25+
->modifySelectableStyle(function (SelectableStyle $style) {
26+
$style->setUnselectedMarker(' ')
27+
->setSelectedMarker('>');
28+
})
2629
->build();
2730

2831
$menu->open();

examples/item-extra-toggling.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
use PhpSchool\CliMenu\CliMenu;
44
use PhpSchool\CliMenu\Builder\CliMenuBuilder;
5+
use PhpSchool\CliMenu\Style\SelectableStyle;
56

67
require_once(__DIR__ . '/../vendor/autoload.php');
78

@@ -20,8 +21,9 @@
2021
->addItem('First Item', $itemCallable)
2122
->addItem('Second Item', $itemCallable)
2223
->addItem('Third Item', $itemCallable)
23-
->setItemExtra('[COMPLETE!]')
24-
->displayExtra()
24+
->modifySelectableStyle(function (SelectableStyle $style) {
25+
$style->setItemExtra('[COMPLETE!]');
26+
})
2527
->addLineBreak('-')
2628
->build();
2729

src/Builder/CliMenuBuilder.php

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PhpSchool\CliMenu\MenuStyle;
1919
use PhpSchool\CliMenu\Style\CheckboxStyle;
2020
use PhpSchool\CliMenu\Style\RadioStyle;
21+
use PhpSchool\CliMenu\Style\SelectableStyle;
2122
use PhpSchool\CliMenu\Terminal\TerminalFactory;
2223
use PhpSchool\Terminal\Terminal;
2324

@@ -89,12 +90,12 @@ public function __construct(Terminal $terminal = null)
8990
$this->style = new MenuStyle($this->terminal);
9091
$this->menu = new CliMenu(null, [], $this->terminal, $this->style);
9192
}
92-
93+
9394
public static function newSubMenu(Terminal $terminal) : self
9495
{
9596
$instance = new self($terminal);
9697
$instance->subMenu = true;
97-
98+
9899
return $instance;
99100
}
100101

@@ -396,22 +397,25 @@ public function setMargin(int $margin) : self
396397
public function setUnselectedMarker(string $marker) : self
397398
{
398399
$this->style->setUnselectedMarker($marker);
400+
$this->menu->getSelectableStyle()->setUnselectedMarker($marker);
399401

400402
return $this;
401403
}
402404

403405
public function setSelectedMarker(string $marker) : self
404406
{
405407
$this->style->setSelectedMarker($marker);
408+
$this->menu->getSelectableStyle()->setSelectedMarker($marker);
406409

407410
return $this;
408411
}
409412

410413
public function setItemExtra(string $extra) : self
411414
{
412415
$this->style->setItemExtra($extra);
416+
$this->menu->getSelectableStyle()->setItemExtra($extra);
413417

414-
//if we customise item extra, it means we most likely want to display it
418+
// if we customise item extra, it means we most likely want to display it
415419
$this->displayExtra();
416420

417421
return $this;
@@ -434,7 +438,7 @@ public function setBorder(int $top, $right = null, $bottom = null, $left = null,
434438
public function setBorderTopWidth(int $width) : self
435439
{
436440
$this->style->setBorderTopWidth($width);
437-
441+
438442
return $this;
439443
}
440444

@@ -497,6 +501,7 @@ public function disableDefaultItems() : self
497501
public function displayExtra() : self
498502
{
499503
$this->style->setDisplaysExtra(true);
504+
$this->menu->getSelectableStyle()->setDisplaysExtra(true);
500505

501506
return $this;
502507
}
@@ -507,7 +512,7 @@ private function itemsHaveExtra(array $items) : bool
507512
return $item->showsItemExtra();
508513
}));
509514
}
510-
515+
511516
public function build() : CliMenu
512517
{
513518
if (!$this->disableDefaultItems) {
@@ -563,6 +568,25 @@ public function modifyRadioStyle(callable $itemCallable) : self
563568
return $this;
564569
}
565570

571+
public function getSelectableStyle() : SelectableStyle
572+
{
573+
return $this->menu->getSelectableStyle();
574+
}
575+
576+
public function setSelectableStyle(SelectableStyle $style) : self
577+
{
578+
$this->menu->setSelectableStyle($style);
579+
580+
return $this;
581+
}
582+
583+
public function modifySelectableStyle(callable $itemCallable) : self
584+
{
585+
$itemCallable($this->menu->getSelectableStyle());
586+
587+
return $this;
588+
}
589+
566590
/**
567591
* Pass styles from current menu to sub-menu
568592
* only if sub-menu style has not be customized
@@ -584,6 +608,12 @@ private function propagateStyles(CliMenu $menu, array $items = [])
584608
$item->setStyle(clone $menu->getRadioStyle());
585609
}
586610

611+
if ($item instanceof SelectableItem
612+
&& !$item->getStyle()->hasChangedFromDefaults()
613+
) {
614+
$item->setStyle(clone $menu->getSelectableStyle());
615+
}
616+
587617
// Apply current style to children, if they are not customized
588618
if ($item instanceof MenuMenuItem) {
589619
$subMenu = $item->getSubMenu();
@@ -600,6 +630,10 @@ private function propagateStyles(CliMenu $menu, array $items = [])
600630
$subMenu->setRadioStyle(clone $menu->getRadioStyle());
601631
}
602632

633+
if (!$subMenu->getSelectableStyle()->hasChangedFromDefaults()) {
634+
$subMenu->setSelectableStyle(clone $menu->getSelectableStyle());
635+
}
636+
603637
$this->propagateStyles($subMenu);
604638
}
605639

src/CliMenu.php

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PhpSchool\CliMenu\Dialogue\Flash;
1717
use PhpSchool\CliMenu\Style\CheckboxStyle;
1818
use PhpSchool\CliMenu\Style\RadioStyle;
19+
use PhpSchool\CliMenu\Style\SelectableStyle;
1920
use PhpSchool\CliMenu\Terminal\TerminalFactory;
2021
use PhpSchool\CliMenu\Util\StringUtil as s;
2122
use PhpSchool\Terminal\InputCharacter;
@@ -47,6 +48,11 @@ class CliMenu
4748
*/
4849
private $radioStyle;
4950

51+
/**
52+
* @var SelectableStyle
53+
*/
54+
private $selectableStyle;
55+
5056
/**
5157
* @var ?string
5258
*/
@@ -102,12 +108,13 @@ public function __construct(
102108
Terminal $terminal = null,
103109
MenuStyle $style = null
104110
) {
105-
$this->title = $title;
106-
$this->items = $items;
107-
$this->terminal = $terminal ?: TerminalFactory::fromSystem();
108-
$this->style = $style ?: new MenuStyle($this->terminal);
109-
$this->checkboxStyle = new CheckboxStyle();
110-
$this->radioStyle = new RadioStyle();
111+
$this->title = $title;
112+
$this->items = $items;
113+
$this->terminal = $terminal ?: TerminalFactory::fromSystem();
114+
$this->style = $style ?: new MenuStyle($this->terminal);
115+
$this->checkboxStyle = new CheckboxStyle();
116+
$this->radioStyle = new RadioStyle();
117+
$this->selectableStyle = new SelectableStyle();
111118

112119
$this->selectFirstItem();
113120
}
@@ -678,6 +685,18 @@ public function setRadioStyle(RadioStyle $style) : self
678685
return $this;
679686
}
680687

688+
public function getSelectableStyle() : SelectableStyle
689+
{
690+
return $this->selectableStyle;
691+
}
692+
693+
public function setSelectableStyle(SelectableStyle $style) : self
694+
{
695+
$this->selectableStyle = $style;
696+
697+
return $this;
698+
}
699+
681700
public function getCurrentFrame() : Frame
682701
{
683702
return $this->currentFrame;

src/MenuItem/SelectableItem.php

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,31 @@
22

33
namespace PhpSchool\CliMenu\MenuItem;
44

5+
use PhpSchool\CliMenu\MenuStyle;
6+
use PhpSchool\CliMenu\Util\StringUtil;
7+
use PhpSchool\CliMenu\Style\SelectableStyle;
8+
59
/**
610
* @author Michael Woodward <mikeymike.mw@gmail.com>
711
*/
812
class SelectableItem implements MenuItemInterface
913
{
10-
use SelectableTrait;
11-
1214
/**
1315
* @var callable
1416
*/
1517
private $selectAction;
1618

19+
private $text = '';
20+
21+
private $showItemExtra = false;
22+
23+
private $disabled = false;
24+
25+
/**
26+
* @var SelectableStyle;
27+
*/
28+
private $style;
29+
1730
public function __construct(
1831
string $text,
1932
callable $selectAction,
@@ -24,6 +37,46 @@ public function __construct(
2437
$this->selectAction = $selectAction;
2538
$this->showItemExtra = $showItemExtra;
2639
$this->disabled = $disabled;
40+
41+
$this->style = new SelectableStyle();
42+
}
43+
44+
/**
45+
* The output text for the item
46+
*/
47+
public function getRows(MenuStyle $style, bool $selected = false) : array
48+
{
49+
$marker = sprintf("%s", $this->style->getMarker($selected));
50+
51+
$length = $this->style->getDisplaysExtra()
52+
? $style->getContentWidth() - (mb_strlen($this->style->getItemExtra()) + 2)
53+
: $style->getContentWidth();
54+
55+
$rows = explode(
56+
"\n",
57+
StringUtil::wordwrap(
58+
sprintf('%s%s', $marker, $this->text),
59+
$length,
60+
sprintf("\n%s", str_repeat(' ', mb_strlen($marker)))
61+
)
62+
);
63+
64+
return array_map(function ($row, $key) use ($style, $length) {
65+
$text = $this->disabled ? $style->getDisabledItemText($row) : $row;
66+
67+
if ($key === 0) {
68+
return $this->showItemExtra
69+
? sprintf(
70+
'%s%s %s',
71+
$text,
72+
str_repeat(' ', $length - mb_strlen($row)),
73+
$this->style->getItemExtra()
74+
)
75+
: $text;
76+
}
77+
78+
return $text;
79+
}, $rows, array_keys($rows));
2780
}
2881

2982
/**
@@ -34,6 +87,18 @@ public function getSelectAction() : ?callable
3487
return $this->selectAction;
3588
}
3689

90+
public function getStyle() : SelectableStyle
91+
{
92+
return $this->style;
93+
}
94+
95+
public function setStyle(SelectableStyle $style) : self
96+
{
97+
$this->style = $style;
98+
99+
return $this;
100+
}
101+
37102
/**
38103
* Return the raw string of text
39104
*/
@@ -49,4 +114,33 @@ public function setText(string $text) : void
49114
{
50115
$this->text = $text;
51116
}
117+
118+
/**
119+
* Can the item be selected
120+
*/
121+
public function canSelect() : bool
122+
{
123+
return !$this->disabled;
124+
}
125+
126+
public function showsItemExtra() : bool
127+
{
128+
return $this->showItemExtra;
129+
}
130+
131+
/**
132+
* Enable showing item extra
133+
*/
134+
public function showItemExtra() : void
135+
{
136+
$this->showItemExtra = true;
137+
}
138+
139+
/**
140+
* Disable showing item extra
141+
*/
142+
public function hideItemExtra() : void
143+
{
144+
$this->showItemExtra = false;
145+
}
52146
}

0 commit comments

Comments
 (0)