In this article, we will explore how to create a command-line interface (CLI) multichoice selector in PowerShell. This selector allows users to navigate through a list of options and make multiple selections using the keyboard. We will break down the code provided and explain the relevant parts to understand how it works.
This is how it will look:
Setting Up Terminal Manipulation Functions
Let's start by setting up the terminal manipulation functions that will allow us to control the appearance of the terminal window.
# Function to clear the terminal
function Clear-Terminal {
$ESC = [char]27
$hideCursor = "${ESC}[?25l"
Write-Host -NoNewline $hideCursor # Hide terminal cursor
[Console]::SetCursorPosition(0, 0)
[Console]::Clear()
}
# Function to close the terminal
Function Close-Terminal {
$ESC = [char]27
$showCursor = "${ESC}[?25h"
Write-Host -NoNewline $showCursor
}
Here, we have two functions: Clear-Terminal
and Close-Terminal
. The Clear-Terminal
function clears the terminal screen and hides the cursor, while the Close-Terminal
function shows the cursor again when we want to exit the script. These functions use escape sequences to hide and show the cursor, respectively. They provide a cleaner and more interactive user experience.
Function to Confirm the Selection and Exit
Next, we have the function responsible for confirming the selected options and exiting the script.
# Function to confirm the selection and exit
function Confirm-Selection {
$selectedOptions.sort()
$selectedOptionNames = $selectedOptions | ForEach-Object { $listOptions[$_] }
Write-Host "`nSelected Options: $($selectedOptionNames -join ", ")`n"
Close-Terminal
exit
}
In this function, the selected options are sorted, and their corresponding names are retrieved from the $listOptions
array. These selected option names are then displayed using Write-Host
. Finally, the Close-Terminal
function is called to show the cursor, and the script exits.
Function to Display the List with Selected Options
Now, let's take a look at the function responsible for rendering the list of options on the screen.
# Function to display the list with the selected options
function Display-List {
Clear-Terminal
Write-Host "Select option(s) (press spacebar to toggle selection):`n"
$listOptions | ForEach-Object -Begin { $index = 0 } -Process {
$isSelected = $selectedOptions.Contains($index)
$prefix = if ($index -eq $selectedOption.Value) { [char]0x276F } else { " " }
$checkbox = if ($isSelected) { [char]0x25C9 } else { [char]0x25CB }
$formattedOption = "$prefix $checkbox $_"
Write-Host $formattedOption
$index++
}
}
This function first calls Clear-Terminal
to clear the terminal screen and hide the cursor. Then, it displays the prompt message using Write-Host
. Next, it iterates over the $listOptions
array using ForEach-Object
.
Inside the loop, it checks whether the current option is selected by using $selectedOptions.Contains($index)
. Based on the selection status, it assigns different values to the $checkbox
variable: "[char]0x25C9" (filled circle) if selected or "[char]0x25CB" (empty circle) if not selected. It also assigns a prefix to highlight the currently selected option using "[char]0x276F" (right-pointing arrow) or a space.
After formatting the option string, it is displayed on the screen using Write-Host
. Finally, the $index
variable is incremented to keep track of the option's index.
Function to Handle Key Press Events
Now, let's explore the function responsible for handling different keypress events.
# Function to handle key press events
function Handle-Key-Press([System.ConsoleKeyInfo]$keyInfo) {
$key = $keyInfo.Key
# Perform actions based on the pressed key
switch ($key) {
"DownArrow" {
$selectedOption.Value = (($selectedOption.Value + 1) % $listOptions.Length)
Display-List($selectedOption.Value, $selectedOptions)
}
"UpArrow" {
$selectedOption.Value = (($selectedOption.Value - 1 + $listOptions.Length) % $listOptions.Length)
Display-List($selectedOption.Value, $selectedOptions)
}
"Spacebar" {
$currentOption = $selectedOption.Value
$isSelected = $selectedOptions.Contains($currentOption)
Write-Host "isSelected: $isSelected"
if ($isSelected) {
$optionIndex = $selectedOptions.IndexOf($currentOption)
$selectedOptions.RemoveAt($optionIndex)
} else {
$selectedOptions.Add($currentOption)
}
Display-List($selectedOption.Value, $selectedOptions)
}
"Enter" {
Confirm-Selection
}
}
}
This function takes a [System.ConsoleKeyInfo]
object, $keyInfo
, as input, which contains information about the pressed key.
Inside the function, a switch statement is used to perform actions based on the pressed key. Here's what each case does:
DownArrow: Moves the currently selected option one position down in the list. It updates the
$selectedOption.Value
by incrementing it by 1 modulo the length of the$listOptions
array. Then, it callsDisplay-List
to refresh the list on the screen with the new selected option.UpArrow: Moves the currently selected option one position up in the list. It updates the
$selectedOption.Value
by decrementing it by 1 and adding the length of the$listOptions
array modulo the length of the$listOptions
array. This calculation ensures that the index wraps around correctly. Then, it callsDisplay-List
to refresh the list on the screen with the new selected option.Spacebar: Toggles the selection of the current option. It checks if the current option is already selected by verifying its presence in the
$selectedOptions
array. If it is selected, it removes it from the array; otherwise, it adds it. Then, it callsDisplay-List
to refresh the list on the screen with the updated selection.Enter: Confirms the selection by calling the
Confirm-Selection
function.
These key press events provide the core functionality of the multichoice selector.
Initializing Variables and Displaying the Initial List
Before we start the main script loop, we need to initialize some variables and display the initial list.
# List options
$listOptions = @("Barcelona", "Munich", "Paris", "Turin")
# Selected options
$selectedOptions = New-Object System.Collections.ArrayList
# Current selected option
$selectedOption = [ref]0
# Display the initial list
Display-List($selectedOption.Value, $selectedOptions)
Here, we define the $listOptions
array, which contains the available
options to choose from. We also create the $selectedOptions
variable as an instance of the System.Collections.ArrayList
class to store the indices of the selected options. The $selectedOption
variable is initialized as a reference to the currently selected option, starting with the index 0.
After initializing the variables, the initial list is displayed on the screen by calling the Display-List
function with the current selection information.
Main Script Loop
Finally, we have the main script loop that continuously waits for keypress events and handles them accordingly.
while ($true) {
$keyInfo = [System.Console]::ReadKey($true)
Handle-Key-Press($keyInfo)
Start-Sleep -Milliseconds 50
}
The loop runs indefinitely (while ($true)
) and uses [System.Console]::ReadKey($true)
to read a keypress without displaying it on the screen. The obtained [System.ConsoleKeyInfo]
object is then passed to the Handle-Key-Press
function for further processing.
A short sleep delay of 50 milliseconds is added using Start-Sleep
to prevent excessive CPU usage during the loop.
By combining all these functions and the main script loop, we create a CLI multichoice selector in PowerShell that allows users to navigate through options, select or deselect them, and confirm their selections.
Feel free to experiment with the code and enhance it to suit your specific requirements. Happy coding!
# Function to clear the terminal
function Clear-Terminal {
$ESC = [char]27
$hideCursor = "${ESC}[?25l"
Write-Host -NoNewline $hideCursor # Hide terminal cursor
[Console]::SetCursorPosition(0, 0)
[Console]::Clear()
}
# Function to close the terminal
Function Close-Terminal {
$ESC = [char]27
$showCursor = "${ESC}[?25h"
Write-Host -NoNewline $showCursor
}
# Function to confirm the selection and exit
function Confirm-Selection {
$selectedOptions.sort()
$selectedOptionNames = $selectedOptions | ForEach-Object { $listOptions[$_] }
Write-Host "`nSelected Options: $($selectedOptionNames -join ", ")`n"
Close-Terminal
exit
}
# Function to display the list with the selected options
function Display-List {
Clear-Terminal
Write-Host "Select option(s) (press spacebar to toggle selection):`n"
$listOptions | ForEach-Object -Begin { $index = 0 } -Process {
$isSelected = $selectedOptions.Contains($index)
$prefix = if ($index -eq $selectedOption.Value) { [char]0x276F } else { " " }
$checkbox = if ($isSelected) { [char]0x25C9 } else { [char]0x25CB }
$formattedOption = "$prefix $checkbox $_"
Write-Host $formattedOption
$index++
}
}
# Function to handle key press events
function Handle-Key-Press([System.ConsoleKeyInfo]$keyInfo) {
$key = $keyInfo.Key
# Perform actions based on the pressed key
switch ($key) {
"DownArrow" {
$selectedOption.Value = (($selectedOption.Value + 1) % $listOptions.Length)
Display-List($selectedOption.Value, $selectedOptions)
}
"UpArrow" {
$selectedOption.Value = (($selectedOption.Value - 1 + $listOptions.Length) % $listOptions.Length)
Display-List($selectedOption.Value, $selectedOptions)
}
"Spacebar" {
$currentOption = $selectedOption.Value
$isSelected = $selectedOptions.Contains($currentOption)
Write-Host "isSelected: $isSelected"
if ($isSelected) {
$optionIndex = $selectedOptions.IndexOf($currentOption)
$selectedOptions.RemoveAt($optionIndex)
} else {
$selectedOptions.Add($currentOption)
}
Display-List($selectedOption.Value, $selectedOptions)
}
"Enter" {
Confirm-Selection
}
}
}
# List options
$listOptions = @("Barcelona", "Munich", "Paris", "Turin")
# Selected options
$selectedOptions = New-Object System.Collections.ArrayList
# Current selected option
$selectedOption = [ref]0
# Display the initial list
Display-List($selectedOption.Value, $selectedOptions)
# Main script
while ($true) {
$keyInfo = [System.Console]::ReadKey($true)
Handle-Key-Press($keyInfo)
Start-Sleep -Milliseconds 50
}
If you have any questions or need further assistance, please let me know in the comments section below or message me Twitter or LinkedIn. Happy coding!.
Top comments (0)