Completed
Pull Request — master (#155)
by Aydin
01:55
created

CliMenuBuilder::addSubMenu()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
cc 2
eloc 12
nc 2
nop 2
1
<?php
2
3
namespace PhpSchool\CliMenu\Builder;
4
5
use PhpSchool\CliMenu\Action\ExitAction;
6
use PhpSchool\CliMenu\Action\GoBackAction;
7
use PhpSchool\CliMenu\MenuItem\AsciiArtItem;
8
use PhpSchool\CliMenu\MenuItem\LineBreakItem;
9
use PhpSchool\CliMenu\MenuItem\MenuItemInterface;
10
use PhpSchool\CliMenu\MenuItem\MenuMenuItem;
11
use PhpSchool\CliMenu\MenuItem\SelectableItem;
12
use PhpSchool\CliMenu\CliMenu;
13
use PhpSchool\CliMenu\MenuItem\StaticItem;
14
use PhpSchool\CliMenu\MenuStyle;
15
use PhpSchool\CliMenu\Terminal\TerminalFactory;
16
use PhpSchool\Terminal\Terminal;
17
18
/**
19
 * @author Michael Woodward <[email protected]>
20
 * @author Aydin Hassan <[email protected]>
21
 */
22
class CliMenuBuilder
23
{
24
    /**
25
     * @var CliMenu
26
     */
27
    private $menu;
28
29
    /**
30
     * @var string
31
     */
32
    private $goBackButtonText = 'Go Back';
33
34
    /**
35
     * @var string
36
     */
37
    private $exitButtonText = 'Exit';
38
39
    /**
40
     * @var MenuStyle
41
     */
42
    private $style;
43
44
    /**
45
     * @var Terminal
46
     */
47
    private $terminal;
48
49
    /**
50
     * @var bool
51
     */
52
    private $disableDefaultItems = false;
53
54
    /**
55
     * @var bool
56
     */
57
    private $disabled = false;
58
59
    /**
60
     * @var bool
61
     */
62
    private $subMenu = false;
63
64
    public function __construct(Terminal $terminal = null)
65
    {
66
        $this->terminal = $terminal ?? TerminalFactory::fromSystem();
67
        $this->style    = new MenuStyle($this->terminal);
68
        $this->menu     = new CliMenu(null, [], $this->terminal, $this->style);
69
    }
70
    
71
    public static function newSubMenu(Terminal $terminal) : self
72
    {
73
        $instance = new self($terminal);
74
        $instance->subMenu = true;
75
        
76
        return $instance;
77
    }
78
79
    public function setTitle(string $title) : self
80
    {
81
        $this->menu->setTitle($title);
82
83
        return $this;
84
    }
85
86
    public function addMenuItem(MenuItemInterface $item) : self
87
    {
88
        $this->menu->addItem($item);
89
90
        return $this;
91
    }
92
93
    public function addItem(
94
        string $text,
95
        callable $itemCallable,
96
        bool $showItemExtra = false,
97
        bool $disabled = false
98
    ) : self {
99
        $this->addMenuItem(new SelectableItem($text, $itemCallable, $showItemExtra, $disabled));
100
101
        return $this;
102
    }
103
104
    public function addItems(array $items) : self
105
    {
106
        foreach ($items as $item) {
107
            $this->addItem(...$item);
0 ignored issues
show
Bug introduced by
The call to addItem() misses a required argument $itemCallable.

This check looks for function calls that miss required arguments.

Loading history...
108
        }
109
110
        return $this;
111
    }
112
113
    public function addStaticItem(string $text) : self
114
    {
115
        $this->addMenuItem(new StaticItem($text));
116
117
        return $this;
118
    }
119
120
    public function addLineBreak(string $breakChar = ' ', int $lines = 1) : self
121
    {
122
        $this->addMenuItem(new LineBreakItem($breakChar, $lines));
123
124
        return $this;
125
    }
126
127
    public function addAsciiArt(string $art, string $position = AsciiArtItem::POSITION_CENTER, string $alt = '') : self
128
    {
129
        $this->addMenuItem(new AsciiArtItem($art, $position, $alt));
130
131
        return $this;
132
    }
133
134
    public function addSubMenu(string $text, callable $callback) : self
135
    {
136
        $builder = self::newSubMenu($this->terminal);
137
138
        $callback($builder);
139
140
        $menu = $builder->build();
141
        $menu->setParent($this->menu);
142
        
143
        //we apply the parent theme if nothing was changed
144
        //if no styles were changed in this sub-menu
145
        if (!$menu->getStyle()->hasChangedFromDefaults()) {
146
            $menu->setStyle($this->menu->getStyle());
147
        }
148
149
        $this->menu->addItem(new MenuMenuItem(
150
            $text,
151
            $menu,
152
            $builder->isMenuDisabled()
153
        ));
154
        
155
        return $this;
156
    }
157
158
    public function addSubMenuFromBuilder(string $text, CliMenuBuilder $builder) : self
159
    {
160
        $menu = $builder->build();
161
        $menu->setParent($this->menu);
162
163
        //we apply the parent theme if nothing was changed
164
        //if no styles were changed in this sub-menu
165
        if (!$menu->getStyle()->hasChangedFromDefaults()) {
166
            $menu->setStyle($this->menu->getStyle());
167
        }
168
169
        $this->menu->addItem(new MenuMenuItem(
170
            $text,
171
            $menu,
172
            $builder->isMenuDisabled()
173
        ));
174
175
        return $this;
176
    }
177
178
    public function addSplitItem(callable $callback) : self
179
    {
180
        $builder = new SplitItemBuilder($this->menu);
181
        
182
        $callback($builder);
183
        
184
        $this->menu->addItem($builder->build());
185
        
186
        return $this;
187
    }
188
189
    /**
190
     * Disable a submenu
191
     *
192
     * @throws \InvalidArgumentException
193
     */
194
    public function disableMenu() : self
195
    {
196
        if (!$this->subMenu) {
197
            throw new \InvalidArgumentException(
198
                'You can\'t disable the root menu'
199
            );
200
        }
201
202
        $this->disabled = true;
203
204
        return $this;
205
    }
206
207
    public function isMenuDisabled() : bool
208
    {
209
        return $this->disabled;
210
    }
211
212
    public function setGoBackButtonText(string $goBackButtonTest) : self
213
    {
214
        $this->goBackButtonText = $goBackButtonTest;
215
216
        return $this;
217
    }
218
219
    public function setExitButtonText(string $exitButtonText) : self
220
    {
221
        $this->exitButtonText = $exitButtonText;
222
223
        return $this;
224
    }
225
226
    public function setBackgroundColour(string $colour, string $fallback = null) : self
227
    {
228
        $this->style->setBg($colour, $fallback);
229
230
        return $this;
231
    }
232
233
    public function setForegroundColour(string $colour, string $fallback = null) : self
234
    {
235
        $this->style->setFg($colour, $fallback);
236
237
        return $this;
238
    }
239
240
    public function setWidth(int $width) : self
241
    {
242
        $this->style->setWidth($width);
243
244
        return $this;
245
    }
246
247
    public function setPadding(int $topBottom, int $leftRight = null) : self
248
    {
249
        $this->style->setPadding($topBottom, $leftRight);
250
251
        return $this;
252
    }
253
254
    public function setPaddingTopBottom(int $topBottom) : self
255
    {
256
        $this->style->setPaddingTopBottom($topBottom);
257
258
        return $this;
259
    }
260
261
    public function setPaddingLeftRight(int $leftRight) : self
262
    {
263
        $this->style->setPaddingLeftRight($leftRight);
264
265
        return $this;
266
    }
267
268
    public function setMarginAuto() : self
269
    {
270
        $this->style->setMarginAuto();
271
272
        return $this;
273
    }
274
275
    public function setMargin(int $margin) : self
276
    {
277
        $this->style->setMargin($margin);
278
279
        return $this;
280
    }
281
282
    public function setUnselectedMarker(string $marker) : self
283
    {
284
        $this->style->setUnselectedMarker($marker);
285
286
        return $this;
287
    }
288
289
    public function setSelectedMarker(string $marker) : self
290
    {
291
        $this->style->setSelectedMarker($marker);
292
293
        return $this;
294
    }
295
296
    public function setItemExtra(string $extra) : self
297
    {
298
        $this->style->setItemExtra($extra);
299
300
        return $this;
301
    }
302
303
    public function setTitleSeparator(string $separator) : self
304
    {
305
        $this->style->setTitleSeparator($separator);
306
307
        return $this;
308
    }
309
310
    public function setBorder(int $top, $right = null, $bottom = null, $left = null, string $colour = null) : self
311
    {
312
        $this->style->setBorder($top, $right, $bottom, $left, $colour);
313
314
        return $this;
315
    }
316
317
    public function setBorderTopWidth(int $width) : self
318
    {
319
        $this->style->setBorderTopWidth($width);
320
        
321
        return $this;
322
    }
323
324
    public function setBorderRightWidth(int $width) : self
325
    {
326
        $this->style->setBorderRightWidth($width);
327
328
        return $this;
329
    }
330
331
    public function setBorderBottomWidth(int $width) : self
332
    {
333
        $this->style->setBorderBottomWidth($width);
334
335
        return $this;
336
    }
337
338
    public function setBorderLeftWidth(int $width) : self
339
    {
340
        $this->style->setBorderLeftWidth($width);
341
342
        return $this;
343
    }
344
345
    public function setBorderColour(string $colour, $fallback = null) : self
346
    {
347
        $this->style->setBorderColour($colour, $fallback);
348
349
        return $this;
350
    }
351
352
    public function getTerminal() : Terminal
353
    {
354
        return $this->terminal;
355
    }
356
357
    private function getDefaultItems() : array
358
    {
359
        $actions = [];
360
        if ($this->subMenu) {
361
            $actions[] = new SelectableItem($this->goBackButtonText, new GoBackAction);
362
        }
363
364
        $actions[] = new SelectableItem($this->exitButtonText, new ExitAction);
365
        return $actions;
366
    }
367
368
    public function disableDefaultItems() : self
369
    {
370
        $this->disableDefaultItems = true;
371
372
        return $this;
373
    }
374
375
    private function itemsHaveExtra(array $items) : bool
376
    {
377
        return !empty(array_filter($items, function (MenuItemInterface $item) {
378
            return $item->showsItemExtra();
379
        }));
380
    }
381
    
382
    public function build() : CliMenu
383
    {
384
        if (!$this->disableDefaultItems) {
385
            $this->menu->addItems($this->getDefaultItems());
386
        }
387
388
        $this->style->setDisplaysExtra($this->itemsHaveExtra($this->menu->getItems()));
389
390
        return $this->menu;
391
    }
392
}
393