PowerShell で、上下キーで選択できるメニューを作りたいと思ったので、作りました。
文字列配列を渡して、文字列かインデックスで結果を受け取ることができるだけの、単純なコマンドです。
逐一画面をクリアして描画するのではなく、その場に現れて、選択したら消える感じになってます。…うまく表現できませんが、画像を見ていただいて、伝わるでしょうか。
使い方
-
menu
(Show-Menu
のエイリアス) に対し引数またはパイプで文字列配列を渡します - 上下キーで選択し、Enter キーで決定すると、結果が文字列で返ります。Esc キーでキャンセルすると、結果は null です
menu 1,2,3,4,5
"foo","bar","baz" | menu
$selected = "A","B","C","D" | menu # 結果を変数へ
cd (ls -Directory | %{ $_.Name } | menu) # 移動先フォルダを選ぶ
notepad (ls -File *.txt | menu) # メモ帳でテキストファイルをどれか開く
-
-Index
オプションを指定すると、結果は文字列でなく数値(選択した項目のインデックス)になります
"foo","bar","baz" | menu -Index # 数値が返る (0~2)
-
-X
、-Y
、-W
オプションで、メニューを表示する位置、メニューの幅を指定できます
ls | menu -X 2 -Y 1 -W 30
-
-Cursor
オプションに指定した文字列を使って、選択項目にカーソルを表示できます
ls | menu -Cursor " > "
- プロファイル等からグローバルスコープで
$MenuPreference
変数を以下のように定義しておくと、メニューの色を設定できます
$MenuPreference = @{
ForegroundColor = "Gray" #非選択項目の文字色
BackgroundColor = "Black" #非選択項目の背景色
SelectionForegroundColor = "DarkGray" #選択項目の文字色
SelectionBackgroundColor = "White" #選択項目の背景色
}
仕組み
- カーソルがウィンドウの下の方にあるとき、メニューが下方にはみ出るかもしれないので、コマンド実行した時点のカーソル位置からメニュー項目分だけ改行してスクロール
- 最初に一度だけメニュー全体を出力
- ↓↓↓ 以降キー入力待ちのループ ↓↓↓
- 選択されている項目を描画する行にカーソルを設定して
Write-Host
で出力 - 前回選択されていた項目は元の色で上書き
- 選択項目が決定されたらメニューの範囲をウィンドウ背景色でクリアして、カーソルを元の位置に設定
参考にした記事
- PowerShell で peco (インタラクティブ・フィルタリングツール)っぽいものを作った
- オセロゲームを作ろう(1)
- PSHostRawUserInterface Class (System.Management.Automation.Host)
- PowerShellで関数の戻りがおかしい件 - フミオトーサン
問題点とか気になっている点
- ウィンドウの行数よりも項目数が多いと、上の方にはみ出してしまって見えなくなってしまう(カーソル選択自体は正常にできている)
- スクロール処理面倒くさそう
- 画面バッファの行数よりも項目数が多いとエラーになってしまう
- しょうがないかなと思う