Completed
Pull Request — master (#203)
by
unknown
02:10
created

SplitItemBuilder::addSubMenu()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 19
rs 9.9332
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();
71
        $this->radioStyle      = new RadioStyle();
72
        $this->selectableStyle = new SelectableStyle();
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
    /**
176
     * Use as
177
     *
178
        ->checkableStyle(function (CheckableStyle $style) {
179
            $style->setMarkerOff('- ');
180
        })
181
     *
182
     * @param callable $itemCallable
183
     * @return $this
184
     */
185
    public function checkableStyle(callable $itemCallable) : self
186
    {
187
        $this->menu->checkableStyle($itemCallable);
188
189
        return $this;
190
    }
191
192
    /**
193
     * Use as
194
     *
195
        ->radioStyle(function (RadioStyle $style) {
196
            $style->setMarkerOff('- ');
197
        })
198
     *
199
     * @param callable $itemCallable
200
     * @return $this
201
     */
202
    public function radioStyle(callable $itemCallable) : self
203
    {
204
        $this->menu->radioStyle($itemCallable);
205
206
        return $this;
207
    }
208
209
    /**
210
     * Use as
211
     *
212
        ->selectableStyle(function (SelectableStyle $style) {
213
            $style->setMarkerOff('- ');
214
        })
215
     *
216
     * @param callable $itemCallable
217
     * @return $this
218
     */
219
    public function selectableStyle(callable $itemCallable) : self
220
    {
221
        $this->menu->selectableStyle($itemCallable);
222
223
        return $this;
224
    }
225
226
    /**
227
     * Create the submenu as a closure which is then unpacked in MenuMenuItem::showSubMenu
228
     * This allows us to wait until all user-provided styles are parsed and apply them to nested items
229
     *
230
     * @param CliMenuBuilder $builder
231
     * @return Closure
232
     */
233
    protected function createMenuClosure(CliMenuBuilder $builder) : Closure
234
    {
235
        return function () use ($builder) {
236
            $menu = $builder->build();
237
238
            $menu->setParent($this->menu);
239
240
            // If user changed this style, persist to the menu so children CheckableItems may use it
241
            if ($this->menu->getCheckableStyle()->getIsCustom()) {
242
                $menu->checkableStyle(function (CheckableStyle $style) {
243
                    $style->fromArray($this->menu->getCheckableStyle()->toArray());
244
                });
245
            }
246
247
            // If user changed this style, persist to the menu so children RadioItems may use it
248
            if ($this->menu->getRadioStyle()->getIsCustom()) {
249
                $menu->radioStyle(function (RadioStyle $style) {
250
                    $style->fromArray($this->menu->getRadioStyle()->toArray());
251
                });
252
            }
253
254
            // If user changed this style, persist to the menu so children SelectableItems may use it
255
            if ($this->menu->getSelectableStyle()->getIsCustom()) {
256
                $menu->selectableStyle(function (SelectableStyle $style) {
257
                    $style->fromArray($this->menu->getSelectableStyle()->toArray());
258
                });
259
            }
260
261
            // This will be filled with user-provided items
262
            foreach ($menu->getItems() as $item) {
263
                if ($item instanceof SelectableInterface && !$item->getStyle()->getIsCustom()) {
264
                    $item->setStyle(clone $menu->getSelectableStyle());
265
                }
266
            }
267
268
            return $menu;
269
        };
270
    }
271
}
272