From b838c4d9510d1644e45d4be23ec33f8c276f0a06 Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Wed, 3 Apr 2019 22:08:17 +0400 Subject: [PATCH 1/2] Implement item shortucts --- examples/shortcuts.php | 33 +++++++++++++++++++ src/Builder/CliMenuBuilder.php | 59 +++++++++++++++++++++++++++++++--- src/CliMenu.php | 21 +++++++++++- 3 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 examples/shortcuts.php diff --git a/examples/shortcuts.php b/examples/shortcuts.php new file mode 100644 index 00000000..80e6f8a1 --- /dev/null +++ b/examples/shortcuts.php @@ -0,0 +1,33 @@ +getSelectedItem()->getText(); +}; + +$menu = (new CliMenuBuilder) + ->setTitle('Basic CLI Menu') + ->addItem('[F]irst Item', $itemCallable) + ->addItem('Se[c]ond Item', $itemCallable) + ->addItem('Third [I]tem', $itemCallable) + ->addSubMenu('[O]ptions', function (CliMenuBuilder $b) { + $b->setTitle('CLI Menu > Options') + ->addItem('First option', function (CliMenu $menu) { + echo sprintf('Executing option: %s', $menu->getSelectedItem()->getText()); + }) + ->addLineBreak('-'); + }) + ->addSplitItem(function (SplitItemBuilder $b) { + $b->addItem('Split Item [1]', function() { echo 'Split Item 1!'; }) + ->addItem('Split Item [2]', function() { echo 'Split Item 2!'; }) + ->addItem('Split Item [3]', function() { echo 'Split Item 3!'; }); + }) + ->addLineBreak('-') + ->build(); + +$menu->open(); diff --git a/src/Builder/CliMenuBuilder.php b/src/Builder/CliMenuBuilder.php index 2bf8853c..23c6ee4c 100644 --- a/src/Builder/CliMenuBuilder.php +++ b/src/Builder/CliMenuBuilder.php @@ -10,6 +10,7 @@ use PhpSchool\CliMenu\MenuItem\MenuMenuItem; use PhpSchool\CliMenu\MenuItem\SelectableItem; use PhpSchool\CliMenu\CliMenu; +use PhpSchool\CliMenu\MenuItem\SplitItem; use PhpSchool\CliMenu\MenuItem\StaticItem; use PhpSchool\CliMenu\MenuStyle; use PhpSchool\CliMenu\Terminal\TerminalFactory; @@ -87,6 +88,8 @@ public function addMenuItem(MenuItemInterface $item) : self { $this->menu->addItem($item); + $this->processItemShortcut($item); + return $this; } @@ -147,12 +150,14 @@ public function addSubMenu(string $text, \Closure $callback) : self $menu->setStyle($this->menu->getStyle()); } - $this->menu->addItem(new MenuMenuItem( + $this->menu->addItem($item = new MenuMenuItem( $text, $menu, $builder->isMenuDisabled() )); - + + $this->processItemShortcut($item); + return $this; } @@ -167,15 +172,57 @@ public function addSubMenuFromBuilder(string $text, CliMenuBuilder $builder) : s $menu->setStyle($this->menu->getStyle()); } - $this->menu->addItem(new MenuMenuItem( + $this->menu->addItem($item = new MenuMenuItem( $text, $menu, $builder->isMenuDisabled() )); + $this->processItemShortcut($item); + return $this; } + private function extractShortcut(string $title) : ?string + { + preg_match('/\[(.)\]/', $title, $match); + return isset($match[1]) ? strtolower($match[1]) : null; + } + + private function processItemShortcut(MenuItemInterface $item) : void + { + $this->processIndividualShortcut($item, function (CliMenu $menu) use ($item) { + $menu->executeAsSelected($item); + }); + } + + private function processSplitItemShortcuts(SplitItem $splitItem) : void + { + foreach ($splitItem->getItems() as $item) { + $this->processIndividualShortcut($item, function (CliMenu $menu) use ($splitItem, $item) { + $current = $splitItem->getSelectedItemIndex(); + + $splitItem->setSelectedItemIndex( + array_search($item, $splitItem->getItems(), true) + ); + + $menu->executeAsSelected($splitItem); + + $splitItem->setSelectedItemIndex($current); + }); + } + } + + private function processIndividualShortcut(MenuItemInterface $item, callable $callback) : void + { + if ($shortcut = $this->extractShortcut($item->getText())) { + $this->menu->addCustomControlMapping( + $shortcut, + $callback + ); + } + } + public function addSplitItem(\Closure $callback) : self { $builder = new SplitItemBuilder($this->menu); @@ -183,8 +230,10 @@ public function addSplitItem(\Closure $callback) : self $callback = $callback->bindTo($builder); $callback($builder); - $this->menu->addItem($builder->build()); - + $this->menu->addItem($splitItem = $builder->build()); + + $this->processSplitItemShortcuts($splitItem); + return $this; } diff --git a/src/CliMenu.php b/src/CliMenu.php index 01cc9f7f..eb9ad5e5 100644 --- a/src/CliMenu.php +++ b/src/CliMenu.php @@ -214,7 +214,7 @@ public function disableDefaultControlMappings() : void public function setDefaultControlMappings(array $defaultControlMappings) : void { $this->defaultControlMappings = $defaultControlMappings; - } + } /** * Adds a custom control mapping @@ -379,6 +379,25 @@ public function getSelectedItem() : MenuItemInterface : $item; } + public function setSelectedItem(MenuItemInterface $item) : void + { + $key = array_search($item, $this->items, true); + + if (false === $key) { + throw new \InvalidArgumentException('Item does not exist in menu'); + } + + $this->selectedItem = $key; + } + + public function executeAsSelected(MenuItemInterface $item) : void + { + $current = $this->items[$this->selectedItem]; + $this->setSelectedItem($item); + $this->executeCurrentItem(); + $this->setSelectedItem($current); + } + /** * Execute the current item */ From ab8e333f9468ace150c54dfdb2471ab65f3a17df Mon Sep 17 00:00:00 2001 From: Aydin Hassan Date: Wed, 3 Apr 2019 22:26:34 +0400 Subject: [PATCH 2/2] Only re-set selected item if there was one --- src/Builder/CliMenuBuilder.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Builder/CliMenuBuilder.php b/src/Builder/CliMenuBuilder.php index 23c6ee4c..483217b2 100644 --- a/src/Builder/CliMenuBuilder.php +++ b/src/Builder/CliMenuBuilder.php @@ -208,7 +208,9 @@ private function processSplitItemShortcuts(SplitItem $splitItem) : void $menu->executeAsSelected($splitItem); - $splitItem->setSelectedItemIndex($current); + if ($current !== null) { + $splitItem->setSelectedItemIndex($current); + } }); } }