diff --git a/src/Builder/CliMenuBuilder.php b/src/Builder/CliMenuBuilder.php index fb7cbfad..5edd7a4e 100644 --- a/src/Builder/CliMenuBuilder.php +++ b/src/Builder/CliMenuBuilder.php @@ -188,12 +188,6 @@ public function addSubMenu(string $text, \Closure $callback) : self $menu = $builder->build(); $menu->setParent($this->menu); - //we apply the parent theme if nothing was changed - //if no styles were changed in this sub-menu - if (!$menu->getStyle()->hasChangedFromDefaults()) { - $menu->setStyle($this->menu->getStyle()); - } - $this->menu->addItem($item = new MenuMenuItem( $text, $menu, @@ -210,12 +204,6 @@ public function addSubMenuFromBuilder(string $text, CliMenuBuilder $builder) : s $menu = $builder->build(); $menu->setParent($this->menu); - //we apply the parent theme if nothing was changed - //if no styles were changed in this sub-menu - if (!$menu->getStyle()->hasChangedFromDefaults()) { - $menu->setStyle($this->menu->getStyle()); - } - $this->menu->addItem($item = new MenuMenuItem( $text, $menu, @@ -556,6 +544,37 @@ public function build() : CliMenu $this->style->setDisplaysExtra($this->itemsHaveExtra($this->menu->getItems())); } + if (!$this->subMenu) { + $this->propagateStyles($this->menu); + } + return $this->menu; } + + /** + * Pass styles from current menu to sub-menu + * only if sub-menu style has not be customized + */ + private function propagateStyles(CliMenu $menu, array $items = []) + { + $currentItems = !empty($items) ? $items : $menu->getItems(); + + foreach ($currentItems as $item) { + // Apply current style to children, if they are not customized + if ($item instanceof MenuMenuItem) { + $subMenu = $item->getSubMenu(); + + if (!$subMenu->getStyle()->hasChangedFromDefaults()) { + $subMenu->setStyle(clone $menu->getStyle()); + } + + $this->propagateStyles($subMenu); + } + + // Apply styles to SplitItem children using current $menu + if ($item instanceof SplitItem) { + $this->propagateStyles($menu, $item->getItems()); + } + } + } } diff --git a/test/Builder/CliMenuBuilderTest.php b/test/Builder/CliMenuBuilderTest.php index 55df7f49..244d5a33 100644 --- a/test/Builder/CliMenuBuilderTest.php +++ b/test/Builder/CliMenuBuilderTest.php @@ -11,6 +11,7 @@ use PhpSchool\CliMenu\MenuItem\MenuMenuItem; use PhpSchool\CliMenu\MenuItem\RadioItem; use PhpSchool\CliMenu\MenuItem\SelectableItem; +use PhpSchool\CliMenu\MenuItem\SplitItem; use PhpSchool\CliMenu\MenuItem\StaticItem; use PhpSchool\Terminal\Terminal; use PHPUnit\Framework\TestCase; @@ -583,17 +584,89 @@ public function testSubMenuInheritsParentsStyle() : void ->expects($this->any()) ->method('getWidth') ->will($this->returnValue(200)); - + $menu = (new CliMenuBuilder($terminal)) ->setBackgroundColour('green') ->addSubMenu('My SubMenu', function (CliMenuBuilder $b) { - $b->addItem('Some Item', function () { + $b->addSubMenu('My SubSubMenu', function (CliMenuBuilder $b) { + $b->addItem('Some Item', function () { + }); }); }) ->build(); - self::assertSame('green', $menu->getItems()[0]->getSubMenu()->getStyle()->getBg()); - self::assertSame($menu->getStyle(), $menu->getItems()[0]->getSubMenu()->getStyle()); + /** @var CliMenu $subMenu1 */ + $subMenu1 = $menu->getItems()[0]->getSubMenu(); + /** @var CliMenu $subMenu2 */ + $subMenu2 = $subMenu1->getItems()[0]->getSubMenu(); + + self::assertSame('green', $subMenu1->getStyle()->getBg()); + self::assertEquals($menu->getStyle(), $subMenu1->getStyle()); + + self::assertSame('green', $subMenu2->getStyle()->getBg()); + self::assertEquals($menu->getStyle(), $subMenu2->getStyle()); + } + + public function testSplitItemSubMenuInheritsParentsStyle() : void + { + $terminal = self::createMock(Terminal::class); + $terminal + ->expects($this->any()) + ->method('getWidth') + ->will($this->returnValue(200)); + + $menu = (new CliMenuBuilder($terminal)) + ->setBackgroundColour('green') + ->addSplitItem(function (SplitItemBuilder $b) { + $b + ->addItem('Item 1', function () { + }) + ->addSubMenu('Submenu 1', function (CliMenuBuilder $b) { + $b->addItem('Item 2', function () { + }); + }) + ; + }) + ->build(); + + /** @var SplitItem $splitItem */ + $splitItem = $menu->getItems()[0]; + /** @var SelectableItem $selectableItem1 */ + $selectableItem1 = $splitItem->getItems()[0]; + /** @var CliMenu $subMenu */ + $subMenu = $splitItem->getItems()[1]->getSubMenu(); + + self::assertSame('green', $subMenu->getStyle()->getBg()); + self::assertEquals($menu->getStyle(), $subMenu->getStyle()); + } + + public function testSubMenuIgnoresParentsStyleIfCustomAndPassesToChildren() : void + { + $terminal = self::createMock(Terminal::class); + $terminal + ->expects($this->any()) + ->method('getWidth') + ->will($this->returnValue(200)); + + $menu = (new CliMenuBuilder($terminal)) + ->setBackgroundColour('green') + ->addSubMenu('My SubMenu', function (CliMenuBuilder $b) { + $b->setBackgroundColour('yellow') + ->addSubMenu('My SubSubMenu', function (CliMenuBuilder $b) { + $b->addItem('Some Item', function () { + }); + }); + }) + ->build(); + + /** @var CliMenu $subMenu1 */ + $subMenu1 = $menu->getItems()[0]->getSubMenu(); + /** @var CliMenu $subMenu2 */ + $subMenu2 = $subMenu1->getItems()[0]->getSubMenu(); + + self::assertSame('green', $menu->getStyle()->getBg()); + self::assertSame('yellow', $subMenu1->getStyle()->getBg()); + self::assertSame('yellow', $subMenu2->getStyle()->getBg()); } public function testSubMenuDoesNotInheritsParentsStyleWhenSubMenuStyleHasAlterations() : void