Passed
Pull Request — master (#203)
by
unknown
01:51
created

SplitItemBuilder::addCheckableItem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 4
dl 0
loc 12
rs 10
1
<?php
2
3
namespace PhpSchool\CliMenu\Builder;
4
5
use Closure;
6
use PhpSchool\CliMenu\CliMenu;
7
use PhpSchool\CliMenu\MenuItem\CheckableItem;
8
use PhpSchool\CliMenu\MenuItem\LineBreakItem;
9
use PhpSchool\CliMenu\MenuItem\MenuMenuItem;
10
use PhpSchool\CliMenu\MenuItem\RadioItem;
11
use PhpSchool\CliMenu\MenuItem\SelectableInterface;
12
use PhpSchool\CliMenu\MenuItem\SelectableItem;
13
use PhpSchool\CliMenu\MenuItem\SplitItem;
14
use PhpSchool\CliMenu\MenuItem\StaticItem;
15
use PhpSchool\CliMenu\Style\CheckableStyle;
16
use PhpSchool\CliMenu\Style\RadioStyle;
17
use PhpSchool\CliMenu\Style\SelectableStyle;
18
19
/**
20
 * @author Aydin Hassan <[email protected]>
21
 */
22
class SplitItemBuilder
23
{
24
    /**
25
     * @var CliMenu
26
     */
27
    private $menu;
28
29
    /**
30
     * @var SplitItem
31
     */
32
    private $splitItem;
33
34
    /**
35
     * Whether or not to auto create keyboard shortcuts for items
36
     * when they contain square brackets. Eg: [M]y item
37
     *
38
     * @var bool
39
     */
40
    private $autoShortcuts = false;
41
42
    /**
43
     * Regex to auto match for shortcuts defaults to looking
44
     * for a single character encased in square brackets
45
     *
46
     * @var string
47
     */
48
    private $autoShortcutsRegex = '/\[(.)\]/';
49
50
    /**
51
     * @var CheckableStyle
52
     */
53
    private $checkableStyle;
54
55
    /**
56
     * @var RadioStyle
57
     */
58
    private $radioStyle;
59
60
    /**
61
     * @var SelectableStyle
62
     */
63
    private $selectableStyle;
64
65
    public function __construct(CliMenu $menu)
66
    {
67
        $this->menu = $menu;
68
        $this->splitItem = new SplitItem();
69
70
        $this->checkableStyle  = new CheckableStyle($menu->getTerminal());
71
        $this->radioStyle      = new RadioStyle($menu->getTerminal());
72
        $this->selectableStyle = new SelectableStyle($menu->getTerminal());
73
    }
74
75
    public function addItem(
76
        string $text,
77
        callable $itemCallable,
78
        bool $showItemExtra = false,
79
        bool $disabled = false
80
    ) : self {
81
        $item = (new SelectableItem($text, $itemCallable, $showItemExtra, $disabled))
82
            ->setStyle($this->menu->getSelectableStyle());
83
84
        $this->splitItem->addItem($item);
85
86
        return $this;
87
    }
88
89
    public function addCheckableItem(
90
        string $text,
91
        callable $itemCallable,
92
        bool $showItemExtra = false,
93
        bool $disabled = false
94
    ) : self {
95
        $item = (new CheckableItem($text, $itemCallable, $showItemExtra, $disabled))
96
            ->setStyle($this->menu->getCheckableStyle());
97
98
        $this->splitItem->addItem($item);
99
100
        return $this;
101
    }
102
103
    public function addRadioItem(
104
        string $text,
105
        callable $itemCallable,
106
        bool $showItemExtra = false,
107
        bool $disabled = false
108
    ) : self {
109
        $item = (new RadioItem($text, $itemCallable, $showItemExtra, $disabled))
110
            ->setStyle($this->menu->getRadioStyle());
111
112
        $this->splitItem->addItem($item);
113
114
        return $this;
115
    }
116
117
    public function addStaticItem(string $text) : self
118
    {
119
        $this->splitItem->addItem(new StaticItem($text));
120
121
        return $this;
122
    }
123
124
    public function addLineBreak(string $breakChar = ' ', int $lines = 1) : self
125
    {
126
        $this->splitItem->addItem(new LineBreakItem($breakChar, $lines));
127
128
        return $this;
129
    }
130
131
    public function addSubMenu(string $text, \Closure $callback) : self
132
    {
133
        $builder = CliMenuBuilder::newSubMenu($this->menu->getTerminal());
134
135
        if ($this->autoShortcuts) {
136
            $builder->enableAutoShortcuts($this->autoShortcutsRegex);
137
        }
138
139
        $callback($builder);
140
141
        $menu = $this->createMenuClosure($builder);
142
143
        $this->splitItem->addItem(new MenuMenuItem(
144
            $text,
145
            $menu,
146
            $builder->isMenuDisabled()
147
        ));
148
        
149
        return $this;
150
    }
151
152
    public function setGutter(int $gutter) : self
153
    {
154
        $this->splitItem->setGutter($gutter);
155
        
156
        return $this;
157
    }
158
159
    public function enableAutoShortcuts(string $regex = null) : self
160
    {
161
        $this->autoShortcuts = true;
162
163
        if (null !== $regex) {
164
            $this->autoShortcutsRegex = $regex;
165
        }
166
167
        return $this;
168
    }
169
    
170
    public function build() : SplitItem
171
    {
172
        return $this->splitItem;
173
    }
174
175
    public function setCheckableStyle(callable $itemCallable) : self
176
    {
177
        $this->menu->setCheckableStyle($itemCallable);
178
179
        return $this;
180
    }
181
182
    public function setRadioStyle(callable $itemCallable) : self
183
    {
184
        $this->menu->setRadioStyle($itemCallable);
185
186
        return $this;
187
    }
188
189
    public function setSelectableStyle(callable $itemCallable) : self
190
    {
191
        $this->menu->setSelectableStyle($itemCallable);
192
193
        return $this;
194
    }
195
196
    /**
197
     * Create the submenu as a closure which is then unpacked in MenuMenuItem::showSubMenu
198
     * This allows us to wait until all user-provided styles are parsed and apply them to nested items
199
     *
200
     * @param CliMenuBuilder $builder
201
     * @return Closure
202
     */
203
    protected function createMenuClosure(CliMenuBuilder $builder) : Closure
204
    {
205
        return function () use ($builder) {
206
            $menu = $builder->build();
207
208
            $menu->setParent($this->menu);
209
210
            // If user changed this style, persist to the menu so children CheckableItems may use it
211
            if ($this->menu->getCheckableStyle()->getIsCustom()) {
212
                $menu->setCheckableStyle(function (CheckableStyle $style) {
213
                    $style->fromArray($this->menu->getCheckableStyle()->toArray());
214
                });
215
            }
216
217
            // If user changed this style, persist to the menu so children RadioItems may use it
218
            if ($this->menu->getRadioStyle()->getIsCustom()) {
219
                $menu->setRadioStyle(function (RadioStyle $style) {
220
                    $style->fromArray($this->menu->getRadioStyle()->toArray());
221
                });
222
            }
223
224
            // If user changed this style, persist to the menu so children SelectableItems may use it
225
            if ($this->menu->getSelectableStyle()->getIsCustom()) {
226
                $menu->setSelectableStyle(function (SelectableStyle $style) {
227
                    $style->fromArray($this->menu->getSelectableStyle()->toArray());
228
                });
229
            }
230
231
            // This will be filled with user-provided items
232
            foreach ($menu->getItems() as $item) {
233
                if ($item instanceof SelectableInterface && !$item->getStyle()->getIsCustom()) {
234
                    $item->setStyle(clone $menu->getSelectableStyle());
235
                }
236
            }
237
238
            return $menu;
239
        };
240
    }
241
}
242