Completed
Pull Request — master (#100)
by
unknown
03:28
created

MenuStyle::setMargin()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
3
namespace PhpSchool\CliMenu;
4
5
use PhpSchool\CliMenu\Exception\InvalidInstantiationException;
6
use PhpSchool\CliMenu\Terminal\TerminalFactory;
7
use PhpSchool\CliMenu\Util\ColourUtil;
8
use PhpSchool\Terminal\Terminal;
9
10
//TODO: B/W fallback
11
12
/**
13
 * @author Michael Woodward <[email protected]>
14
 */
15
class MenuStyle
16
{
17
    /**
18
     * @var Terminal
19
     */
20
    protected $terminal;
21
22
    /**
23
     * @var string
24
     */
25
    protected $fg;
26
27
    /**
28
     * @var string
29
     */
30
    protected $bg;
31
32
    /**
33
     * @var int
34
     */
35
    protected $width;
36
37
    /**
38
     * @var int
39
     */
40
    protected $padding;
41
42
    /**
43
     * @var int
44
     */
45
    protected $margin;
46
47
    /**
48
     * @var int
49
     */
50
    protected $contentWidth;
51
52
    /**
53
     * @var string
54
     */
55
    private $selectedMarker;
56
57
    /**
58
     * @var string
59
     */
60
    private $unselectedMarker;
61
62
    /**
63
     * @var string
64
     */
65
    private $itemExtra;
66
67
    /**
68
     * @var bool
69
     */
70
    private $displaysExtra;
71
72
    /**
73
     * @var string
74
     */
75
    private $titleSeparator;
76
77
    /**
78
     * @var string
79
     */
80
    private $coloursSetCode;
81
82
    /**
83
     * @var string
84
     */
85
    private $invertedColoursSetCode = "\033[7m";
86
87
    /**
88
     * @var string
89
     */
90
    private $invertedColoursUnsetCode = "\033[27m";
91
92
    /**
93
     * @var string
94
     */
95
    private $coloursResetCode = "\033[0m";
96
97
    /**
98
     * @var int
99
     */
100
    private $borderTopWidth;
101
102
    /**
103
     * @var int
104
     */
105
    private $borderRightWidth;
106
107
    /**
108
     * @var int
109
     */
110
    private $borderBottomWidth;
111
112
    /**
113
     * @var int
114
     */
115
    private $borderLeftWidth;
116
117
    /**
118
     * @var string
119
     */
120
    private $borderColour = 'white';
121
122
    /**
123
     * @var array
124
     */
125
    private $borderTopRows = [];
126
127
    /**
128
     * @var array
129
     */
130
    private $borderBottomRows = [];
131
132
    /**
133
     * @var bool
134
     */
135
    private $marginAuto = false;
136
137
    /**
138
     * Default Values
139
     *
140
     * @var array
141
     */
142
    private static $defaultStyleValues = [
143
        'fg' => 'white',
144
        'bg' => 'blue',
145
        'width' => 100,
146
        'padding' => 2,
147
        'margin' => 2,
148
        'selectedMarker' => '●',
149
        'unselectedMarker' => '○',
150
        'itemExtra' => '✔',
151
        'displaysExtra' => false,
152
        'titleSeparator' => '=',
153
        'borderTopWidth' => 0,
154
        'borderRightWidth' => 0,
155
        'borderBottomWidth' => 0,
156
        'borderLeftWidth' => 0,
157
        'borderColour' => 'white',
158
        'marginAuto' => false,
159
    ];
160
161
    public static function getDefaultStyleValues() : array
162
    {
163
        return static::$defaultStyleValues;
164
    }
165
166
    /**
167
     * @var array
168
     */
169
    private static $availableForegroundColors = array(
170
        'black'   => 30,
171
        'red'     => 31,
172
        'green'   => 32,
173
        'yellow'  => 33,
174
        'blue'    => 34,
175
        'magenta' => 35,
176
        'cyan'    => 36,
177
        'white'   => 37,
178
        'default' => 39,
179
    );
180
181
    /**
182
     * @var array
183
     */
184
    private static $availableBackgroundColors = array(
185
        'black'   => 40,
186
        'red'     => 41,
187
        'green'   => 42,
188
        'yellow'  => 43,
189
        'blue'    => 44,
190
        'magenta' => 45,
191
        'cyan'    => 46,
192
        'white'   => 47,
193
        'default' => 49,
194
    );
195
196
    /**
197
     * @var array
198
     */
199
    private static $availableOptions = array(
200
        'bold'       => array('set' => 1, 'unset' => 22),
201
        'dim'        => array('set' => 2, 'unset' => 22),
202
        'underscore' => array('set' => 4, 'unset' => 24),
203
        'blink'      => array('set' => 5, 'unset' => 25),
204
        'reverse'    => array('set' => 7, 'unset' => 27),
205
        'conceal'    => array('set' => 8, 'unset' => 28)
206
    );
207
208
    /**
209
     * Initialise style
210
     */
211
    public function __construct(Terminal $terminal = null)
212
    {
213
        $this->terminal = $terminal ?: TerminalFactory::fromSystem();
214
215
        $this->fg = static::$defaultStyleValues['fg'];
216
        $this->bg = static::$defaultStyleValues['bg'];
217
        
218
        $this->generateColoursSetCode();
219
        
220
        $this->setWidth(static::$defaultStyleValues['width']);
221
        $this->setPadding(static::$defaultStyleValues['padding']);
222
        $this->setMargin(static::$defaultStyleValues['margin']);
223
        $this->setSelectedMarker(static::$defaultStyleValues['selectedMarker']);
224
        $this->setUnselectedMarker(static::$defaultStyleValues['unselectedMarker']);
225
        $this->setItemExtra(static::$defaultStyleValues['itemExtra']);
226
        $this->setDisplaysExtra(static::$defaultStyleValues['displaysExtra']);
227
        $this->setTitleSeparator(static::$defaultStyleValues['titleSeparator']);
228
        $this->setBorderTopWidth(static::$defaultStyleValues['borderTopWidth']);
229
        $this->setBorderRightWidth(static::$defaultStyleValues['borderRightWidth']);
230
        $this->setBorderBottomWidth(static::$defaultStyleValues['borderBottomWidth']);
231
        $this->setBorderLeftWidth(static::$defaultStyleValues['borderLeftWidth']);
232
        $this->setBorderColour(static::$defaultStyleValues['borderColour']);
233
    }
234
235
    public function getDisabledItemText(string $text) : string
236
    {
237
        return sprintf(
238
            "\033[%sm%s\033[%sm",
239
            self::$availableOptions['dim']['set'],
240
            $text,
241
            self::$availableOptions['dim']['unset']
242
        );
243
    }
244
245
    /**
246
     * Generates the ansi escape sequence to set the colours
247
     */
248
    private function generateColoursSetCode() : void
249
    {
250
        if (!ctype_digit($this->fg)) {
251
            $fgCode = self::$availableForegroundColors[$this->fg];
252
        } else {
253
            $fgCode = sprintf("38;5;%s", $this->fg);
254
        }
255
256
        if (!ctype_digit($this->bg)) {
257
            $bgCode = self::$availableBackgroundColors[$this->bg];
258
        } else {
259
            $bgCode = sprintf("48;5;%s", $this->bg);
260
        }
261
262
        $this->coloursSetCode = sprintf("\033[%s;%sm", $fgCode, $bgCode);
263
    }
264
265
    /**
266
     * Get the colour code for Bg and Fg
267
     */
268
    public function getColoursSetCode() : string
269
    {
270
        return $this->coloursSetCode;
271
    }
272
273
    /**
274
     * Get the inverted escape sequence (used for selected elements)
275
     */
276
    public function getInvertedColoursSetCode() : string
277
    {
278
        return $this->invertedColoursSetCode;
279
    }
280
281
    /**
282
     * Get the inverted escape sequence (used for selected elements)
283
     */
284
    public function getInvertedColoursUnsetCode() : string
285
    {
286
        return $this->invertedColoursUnsetCode;
287
    }
288
289
    /**
290
     * Get the escape sequence used to reset colours to default
291
     */
292
    public function getColoursResetCode() : string
293
    {
294
        return $this->coloursResetCode;
295
    }
296
297
    /**
298
     * Calculate the contents width
299
     */
300
    protected function calculateContentWidth() : void
301
    {
302
        $this->contentWidth = $this->width
303
            - ($this->padding * 2)
304
            - ($this->borderRightWidth + $this->borderLeftWidth);
305
    }
306
307
    public function getFg()
308
    {
309
        return $this->fg;
310
    }
311
312
    public function setFg(string $fg, string $fallback = null) : self
313
    {
314
        $this->fg = ColourUtil::validateColour(
315
            $this->terminal,
316
            $fg,
317
            $fallback
318
        );
319
        $this->generateColoursSetCode();
320
321
        return $this;
322
    }
323
324
    public function getBg()
325
    {
326
        return $this->bg;
327
    }
328
329
    public function setBg(string $bg, string $fallback = null) : self
330
    {
331
        $this->bg = ColourUtil::validateColour(
332
            $this->terminal,
333
            $bg,
334
            $fallback
335
        );
336
        $this->generateColoursSetCode();
337
338
        return $this;
339
    }
340
341
    public function getWidth() : int
342
    {
343
        return $this->width;
344
    }
345
346
    public function setWidth(int $width) : self
347
    {
348
        if ($width >= $this->terminal->getWidth()) {
349
            $width = $this->terminal->getWidth();
350
        }
351
352
        $this->width = $width;
353
        if ($this->marginAuto) {
354
            $this->setMarginAuto();
355
        }
356
357
        $this->calculateContentWidth();
358
        $this->generateBorderRows();
359
360
        return $this;
361
    }
362
363
    public function getPadding() : int
364
    {
365
        return $this->padding;
366
    }
367
368
    public function setPadding(int $padding) : self
369
    {
370
        $this->padding = $padding;
371
372
        $this->calculateContentWidth();
373
374
        return $this;
375
    }
376
377
    public function getMargin() : int
378
    {
379
        return $this->margin;
380
    }
381
382
    public function setMarginAuto() : self
383
    {
384
        $this->marginAuto = true;
385
        $this->margin = floor(($this->terminal->getWidth() - $this->width) / 2);
386
        
387
        return $this;
388
    }
389
390
    public function setMargin(int $margin) : self
391
    {
392
        $this->marginAuto = false;
393
        $this->margin = $margin;
394
395
        return $this;
396
    }
397
398
    public function getContentWidth() : int
399
    {
400
        return $this->contentWidth;
401
    }
402
403
    /**
404
     * Get padding for right had side of content
405
     */
406
    public function getRightHandPadding(int $contentLength) : int
407
    {
408
        return $this->getContentWidth() - $contentLength + $this->getPadding();
409
    }
410
411
    public function getSelectedMarker() : string
412
    {
413
        return $this->selectedMarker;
414
    }
415
416
    public function setSelectedMarker(string $marker) : self
417
    {
418
        $this->selectedMarker = mb_substr($marker, 0, 1);
419
420
        return $this;
421
    }
422
423
    public function getUnselectedMarker() : string
424
    {
425
        return $this->unselectedMarker;
426
    }
427
428
    public function setUnselectedMarker(string $marker) : self
429
    {
430
        $this->unselectedMarker = mb_substr($marker, 0, 1);
431
432
        return $this;
433
    }
434
435
    /**
436
     * Get the correct marker for the item
437
     */
438
    public function getMarker(bool $selected) : string
439
    {
440
        return $selected ? $this->selectedMarker : $this->unselectedMarker;
441
    }
442
443
    public function setItemExtra(string $itemExtra) : self
444
    {
445
        $this->itemExtra = $itemExtra;
446
447
        return $this;
448
    }
449
450
    public function getItemExtra() : string
451
    {
452
        return $this->itemExtra;
453
    }
454
455
    public function getDisplaysExtra() : bool
456
    {
457
        return $this->displaysExtra;
458
    }
459
460
    public function setDisplaysExtra(bool $displaysExtra) : self
461
    {
462
        $this->displaysExtra = $displaysExtra;
463
464
        return $this;
465
    }
466
467
    public function getTitleSeparator() : string
468
    {
469
        return $this->titleSeparator;
470
    }
471
472
    public function setTitleSeparator(string $actionSeparator) : self
473
    {
474
        $this->titleSeparator = $actionSeparator;
475
476
        return $this;
477
    }
478
479
    private function generateBorderRows() : void
480
    {
481
        $borderRow = sprintf(
482
            "%s%s%s%s%s\n",
483
            str_repeat(' ', $this->margin),
484
            $this->getBorderColourCode(),
485
            str_repeat(' ', $this->width),
486
            $this->coloursResetCode,
487
            str_repeat(' ', $this->margin)
488
        );
489
490
        $this->borderTopRows = array_fill(0, $this->borderTopWidth, $borderRow)
491
        $this->borderBottomRows = array_fill(0, $this->borderBottomWidth, $borderRow)
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_VARIABLE
Loading history...
492
    }
493
494
    public function getBorderTopRows() : array
495
    {
496
        return $this->borderTopRows;
497
    }
498
499
    public function getBorderBottomRows() : array
500
    {
501
        return $this->borderBottomRows;
502
    }
503
504
    /**
505
     * Shorthand function to set all borders values at once
506
     */
507
    public function setBorder(
508
        int $topWidth,
509
        $rightWidth = null,
510
        $bottomWidth = null,
511
        $leftWidth = null,
512
        string $colour = null
513
    ) : self {
514
        if (!is_int($rightWidth)) {
515
            $colour = $rightWidth;
516
            $rightWidth = $bottomWidth = $leftWidth = $topWidth;
517
        } elseif (!is_int($bottomWidth)) {
518
            $colour = $bottomWidth;
519
            $bottomWidth = $topWidth;
520
            $leftWidth = $rightWidth;
521
        } elseif (!is_int($leftWidth)) {
522
            $colour = $leftWidth;
523
            $leftWidth = $rightWidth;
524
        }
525
526
        $this->borderTopWidth = $topWidth;
527
        $this->borderRightWidth = $rightWidth;
528
        $this->borderBottomWidth = $bottomWidth;
529
        $this->borderLeftWidth = $leftWidth;
530
531
        if (is_string($colour)) {
532
            $this->setBorderColour($colour);
533
        } elseif ($colour !== null) {
534
            throw new \InvalidArgumentException('Invalid colour');
535
        }
536
537
        $this->calculateContentWidth();
538
        $this->generateBorderRows();
539
540
        return $this;
541
    }
542
543
    public function setBorderTopWidth(int $width) : self
544
    {
545
        $this->borderTopWidth = $width;
546
547
        $this->generateBorderRows();
548
549
        return $this;
550
    }
551
552
    public function setBorderRightWidth(int $width) : self
553
    {
554
        $this->borderRightWidth = $width;
555
        $this->calculateContentWidth();
556
557
        return $this;
558
    }
559
560
    public function setBorderBottomWidth(int $width) : self
561
    {
562
        $this->borderBottomWidth = $width;
563
564
        $this->generateBorderRows();
565
566
        return $this;
567
    }
568
569
    public function setBorderLeftWidth(int $width) : self
570
    {
571
        $this->borderLeftWidth = $width;
572
        $this->calculateContentWidth();
573
574
        return $this;
575
    }
576
577
    public function setBorderColour(string $colour, $fallback = null) : self
578
    {
579
        $this->borderColour = ColourUtil::validateColour(
580
            $this->terminal,
581
            $colour,
582
            $fallback
583
        );
584
585
        $this->generateBorderRows();
586
587
        return $this;
588
    }
589
590
    public function getBorderTopWidth() : int
591
    {
592
        return $this->borderTopWidth;
593
    }
594
595
    public function getBorderRightWidth() : int
596
    {
597
        return $this->borderRightWidth;
598
    }
599
600
    public function getBorderBottomWidth() : int
601
    {
602
        return $this->borderBottomWidth;
603
    }
604
605
    public function getBorderLeftWidth() : int
606
    {
607
        return $this->borderLeftWidth;
608
    }
609
610
    public function getBorderColour() : string
611
    {
612
        return $this->borderColour;
613
    }
614
615
    public function getBorderColourCode() : string
616
    {
617
        if (ctype_digit($this->borderColour)) {
618
            $borderColourCode = self::$availableBackgroundColors[$this->bg];
619
        } else {
620
            $borderColourCode = sprintf("48;5;%s", $this->bg);
621
        }
622
623
        return sprintf("\033[%sm", $borderColourCode);
624
    }
625
}
626