Skip to content

Instantly share code, notes, and snippets.

@voidproc
Last active May 27, 2020 08:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save voidproc/44b199160141f264da192807795f1dfb to your computer and use it in GitHub Desktop.
Save voidproc/44b199160141f264da192807795f1dfb to your computer and use it in GitHub Desktop.
$ErrorActionPreference = "Stop"
# Default settings
if ($MenuPreference -eq $null)
{
$MenuPreference = @{
ForegroundColor = "DarkGray"
BackgroundColor = "Black"
SelectionForegroundColor = "White"
SelectionBackgroundColor = "Blue"
}
}
$FgColor = $MenuPreference.ForegroundColor
$BgColor = $MenuPreference.BackgroundColor
$SelFgColor = $MenuPreference.SelectionForegroundColor
$SelBgColor = $MenuPreference.SelectionBackgroundColor
$rui = (Get-Host).UI.RawUI
$tmp_coord = New-Object System.Management.Automation.Host.Coordinates(0, 0)
function New-BgBuffer
{
$rui.NewBufferCellArray(" ", "$($rui.ForegroundColor)", "$($rui.BackgroundColor)")
}
function New-Coord
{
Param($x, $y)
New-Object System.Management.Automation.Host.Coordinates($x, $y)
}
function New-Rect
{
Param($left, $top, $right, $bottom)
New-Object System.Management.Automation.Host.Rectangle($left, $top, $right, $bottom)
}
function Set-CursorPos
{
Param($x, $y)
$tmp_coord.X = $x
$tmp_coord.Y = $y
(Get-Host).UI.RawUI.CursorPosition = $tmp_coord
}
function Get-SubStringBytes
{
Param($text, $start, $length)
$encoding = [System.Text.Encoding]::GetEncoding("Shift_JIS")
$bytes = $encoding.GetBytes($text)
$encoding.GetString($bytes, $start, $length)
}
function Write-Item
{
Param(
[string]$Text,
[int]$X,
[int]$Y,
[int]$W,
[bool]$Select
)
Set-CursorPos $X $Y
$buf = $rui.NewBufferCellArray($Text, "White", "Black")
$len = $buf.Length
if ($len -gt $W) {
if ($buf[0, ($W-1)].BufferCellType -eq "Leading")
{
$Text = Get-SubStringBytes $Text 0 ($W-1)
$len = $W - 1
}
else
{
$Text = Get-SubStringBytes $Text 0 $W
$len = $W
}
}
$Text = "$Text$(" " * [Math]::Max(0, $W - $len))"
$fg, $bg = @( @( $FgColor, $BgColor ), @( $SelFgColor, $SelBgColor) )[[int]$Select]
Write-Host $Text -ForegroundColor $fg -BackgroundColor $bg -NoNewline | Out-Host
}
function Show-Menu
{
Param(
[Parameter(ValueFromPipeline=$true)]
[string[]]
$Items,
[int]
$X = 0,
[int]
$Y = 0,
[int]
$W = 0,
[string]
$Cursor = "",
[ScriptBlock]
$OnInit = {},
[ScriptBlock]
$OnDraw = {
Param($idx)
},
[ScriptBlock]
$OnKeyPress = {
Param($idx, $keyinfo)
$idx, ""
},
[ScriptBlock]
$OnClose = {},
[switch]
$Index
)
begin
{
$Script:MenuItems = New-Object System.Collections.Generic.List[string]
}
process
{
if ($null -ne $Items)
{
$Items | ForEach-Object { $MenuItems.Add($_) }
}
}
end
{
if ($MenuItems.Count -eq 0)
{
return
}
$CursorSize = $rui.CursorSize
#$rui.CursorSize = 0
# Base position
$base_y = $rui.CursorPosition.Y + $Y
if (!$W) { $W = $rui.WindowSize.Width }
$W = [Math]::Min($W, $rui.WindowSize.Width - $X)
& $OnInit
# Draw menu BG
$menurect = New-Rect $X $base_y ($X + $W) ($base_y + $MenuItems.Count-1)
# BG
$bgbuf = New-BgBuffer
# Selected item
$idx = 0
$idxp = 0
$cancel = $false
# Cursor
$cur = @( (" "*$Cursor.Length), $Cursor )
# Scroll
Set-CursorPos $X ($base_y + $MenuItems.Count)
# Draw all
$MenuItems | ForEach-Object { $i = 0 } {
Write-Item "$($cur[0])$_" $X ($base_y + $i) $W $false
$i++
}
:readkeyloop while ($true)
{
# Draw item
Write-Item "$($cur[0])$($MenuItems[$idxp])" $X ($base_y + $idxp) $W $false
Write-Item "$($cur[1])$($MenuItems[$idx])" $X ($base_y + $idx) $W $true
Set-CursorPos $X ($base_y + $MenuItems.Count)
. $OnDraw $idx
$keyinfo = [Console]::ReadKey($true)
$idxp = $idx
$idx, $op = . $OnKeyPress $idx $keyinfo
switch ($op.ToLower())
{
"select" {
break readkeyloop
}
"cancel" {
$cancel = $true
break readkeyloop
}
}
switch ($keyinfo.Key)
{
"UpArrow" {
$idxp = $idx
if (--$idx -lt 0) { $idx = $MenuItems.Count-1 }
}
"DownArrow" {
$idxp = $idx
if (++$idx -ge $MenuItems.Count) { $idx = 0 }
}
"Enter" {
break readkeyloop
}
"Escape" {
$cancel = $true
break readkeyloop
}
}
}
. $OnClose
# Clear menu
$rui.SetBufferContents($menurect, $bgbuf)
# Back Cursor
Set-CursorPos 0 ($base_y - $Y)
$rui.CursorSize = $CursorSize
# Result
if (-not $cancel)
{
if ($Index)
{
$idx
}
else {
$MenuItems[$idx]
}
}
}
}
Set-Alias -Name menu -Value Show-Menu
Export-ModuleMember -Function Show-Menu -Alias menu
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment