Add-Type -AssemblyName System.Drawing Add-Type @" using System; using System.Runtime.InteropServices; public class NativeMethods { [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); } "@ # 定数定義 $WM_NCLBUTTONDOWN = 0xA1 $HTCAPTION = 0x2 # メニュー設定ファイル $csvPath = "$PSScriptRoot\menutable.csv" #パネルグループ分け件数 $btnCount = 100 $flags = [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Instance $script:menutable = @() $fontzoom = 1.0 # サイズ倍率 $MinButtonWidth = [int](54 * $fontzoom) $MinButtonHeight = [int](54 * $fontzoom) $BrowserPath = "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" # 格納フォーム寸法 $script:fold = [PSCustomObject]@{ x = 550 y = 100 w = 50 h = 50 } # 展開フォーム寸法 $script:expand = [PSCustomObject]@{ x = 200 y = 100 w = 400 h = 300 } # ボタンの種類に応じて文字色を設定 $colorMap = @{ "PowerShell" = "#09c" "実行" = "#09d" "実行2" = "#09d" "フォルダ" = "#c90" "開く" = "#ccc" "URL" = "#0c9" "" = "#333" } $FontLevel = @{ "L" = 26 * $fontzoom "M" = 16 * $fontzoom "S" = 14 * $fontzoom "SS" = 12 * $fontzoom } $iconSize = 48 * $fontzoom # リセット復元用 $script:currentGroupId = 1 # 編集モードフラグ $script:isEditMode = $false $script:isMenuPanel = $false $script:editMenuBound = 0 # 展開フォームフラグ $script:isExpand = $false # 展開ボタン移動モード $script:isMoveBtn = $false # グループ化 $groupPanels = @{} $groupRadios = @{} # ツールチップオブジェクト $tooltip = New-Object System.Windows.Forms.ToolTip # 固定アイコン $script:explorericon = [System.Drawing.Icon]::ExtractAssociatedIcon("$env:windir\explorer.exe") $script:powershellicon = [System.Drawing.Icon]::ExtractAssociatedIcon((Get-Command "powershell.exe").Source) # エンコード指定 $psversion = $PSVersionTable.PSVersion.Major if ( $psversion -ge 6 ) { $csvencode = "Shift_JIS" } else { $csvencode = "Default" } # タイマー(共通) $script:hoverTimer = New-Object Windows.Forms.Timer $script:hoverTimer.Interval = 300 $script:currentRadio = $null $script:hoverTimer.Add_Tick({ if ( $script:currentRadio -ne $null ) { $script:currentRadio.Checked = $true $script:hoverTimer.Stop() } }) ########## # 関数 # 関数:メッセージボックス function MsgBox ($message, $title = "通知", $buttons = "OK", $icon = "None", $defaultButton = "Button1", $timeout = 0) { return [System.Windows.Forms.MessageBox]::Show($message, $title, $buttons, $icon, $defaultButton, $timeout) } # 関数:ひとつ上のフォルダを開く function Open-ParentFolder { param($Path) Show-FoldForm try{ if ([System.IO.Path]::GetExtension($Path) -eq "") { $folder = $Path } else { $folder = Split-Path $Path -Parent } & explorer.exe $folder } catch { Write-Host "一つ上のフォルダを開けなかった:$folder" } } # 関数:グループ選択移動フォーム function Show-GroupSelector { param($groupPanels) $groupSelectForm = New-Object System.Windows.Forms.Form $groupSelectForm.Text = "グループ選択" $groupSelectForm.Size = "275,$($groupPanels.Keys.Count * 50 + 150)" $groupSelectForm.StartPosition = 'CenterParent' $panel = New-Object System.Windows.Forms.FlowLayoutPanel $panel.Dock = 'Fill' $panel.AutoScroll = $true $groupSelectForm.Controls.Add($panel) foreach ($key in ($groupPanels.Keys | Sort-Object)) { $btn = New-Object System.Windows.Forms.Button $btn.Text = "$key" $btn.Font = "Consolas,28" $btn.Tag = $key $btn.Width = 250 $btn.Height = 40 $btn.Add_Click({ param($s, $e) Write-Host "選択されたグループ: $($s.Tag)" $groupSelectForm.DialogResult = 'OK' $groupSelectForm.Tag = $s.Tag # 選択結果を保持 $groupSelectForm.Close() }) $panel.Controls.Add($btn) } # パネル追加ボタン $btnAddPanel = New-Object System.Windows.Forms.Button $btnAddPanel.Text = "追加" $btnAddPanel.Font = "Consolas,20" $btnAddPanel.Size = New-Object Drawing.Size(250,40) $btnAddPanel.Add_Click({ $gid = Add-NewPanel Write-Host "選択されたグループ: $($s.Tag)" $groupSelectForm.DialogResult = 'OK' $groupSelectForm.Tag = $gid # 選択結果を保持 $groupSelectForm.Close() }) $panel.Controls.Add($btnAddPanel) # キャンセルボタン $cancel = New-Object System.Windows.Forms.Button $cancel.Text = "キャンセル" $cancel.Width = 250 $cancel.Height = 40 $cancel.Add_Click({ $groupSelectForm.DialogResult = 'Cancel'; $groupSelectForm.Close() }) $panel.Controls.Add($cancel) $groupSelectForm.TopMost = $true $result = $groupSelectForm.ShowDialog() if ($result -eq 'OK') { return $groupSelectForm.Tag # 選択されたキー } else { return $null } } function Set-FormSizeSettings { if ($script:isEditMode) { $topBound = 30 $leftBound = 8 } else { $topBound = 0 $leftBound = 0 } if ($script:isExpand) { $script:expand.x = $script:form.Location.X + $script:editMenuBound + $leftBound $script:expand.y = $script:form.Location.Y + $topBound $script:expand.w = $script:form.Size.Width - $script:editMenuBound - ($leftBound * 2) $script:expand.h = $script:form.Size.Height - ($topBound + $leftBound) $script:fold.x = $script:expand.x + $script:expand.w - $script:fold.w $script:fold.y = $script:expand.y } else { $script:expand.x = $script:form.Left - $script:expand.w + $script:fold.w + $leftBound $script:expand.y = $script:form.Top + $topBound $script:fold.x = $script:form.Location.X + $leftBound $script:fold.y = $script:form.Location.Y + $topBound } } # 関数:フォームサイズ変更 展開サイズ function Show-ExpandForm { # if($script:timer) { # $script:timer.Stop() # $script:timer.Dispose() # $script:timer = $null # } # # $script:timer = New-Object Windows.Forms.Timer # $script:timer.Interval = 10 # $script:step = 10 # $script:targetX = $script:expand.x - $script:editMenuBound # $script:targetY = $script:expand.y # $script:targetW = $script:expand.w + $script:editMenuBound # $script:targetH = $script:expand.h # # # $script:timer.Add_Tick({ # # $currentX = $script:form.Left # $currentY = $script:form.Top # $currentW = $script:form.Width # $currentH = $script:form.Height # # $nextX = if ($currentX -gt $script:targetX) { $currentX - $step } else { $script:targetX } # $nextY = $script:targetY # $nextW = if ($currentW -lt $script:targetW) { $currentW + $step } else { $script:targetW } # #$nextH = if ($currentH -lt $script:targetH) { $currentH + ($step * ($currentH / $currentW)) } else { $script:targetH } # $nextH = $script:targetH # # $script:form.SetBounds($nextX, $nextY, $nextW, $nextH) # #Write-Host "$nextX, $nextY, $nextW, $nextH $script:targetX, $script:targetY, $script:targetW, $script:targetH" # Write-Host "$($nextX - $script:targetX), $($nextY - $script:targetY), $($script:targetW - $nextW), $($script:targetH - $nextH)" # if ($nextX -le $script:targetX -and $nextY -ge $script:targetY -and $nextW -ge $script:targetW -and $nextH -ge $script:targetH) { # $script:form.SetBounds($script:targetX, $script:targetY, $script:targetW, $script:targetH) # $script:timer.Stop() # $script:timer.Dispose() # $script:timer = $null # } #}) #$script:timer.Start() $script:isExpand = $true $targetX = $script:expand.x - $script:editMenuBound $targetY = $script:expand.y $targetW = $script:expand.w + $script:editMenuBound $targetH = $script:expand.h $script:form.SuspendLayout() $expandFormBtn.Visible = $false $expandFormBtn.SendToBack() $script:form.SetBounds($targetX, $targetY, $targetW, $targetH) $script:form.ResumeLayout() $editBtnItem.Enabled = $true $editMenuItem.Enabled = $true } # 関数:フォームサイズ変更 格納サイズ function Show-FoldForm { if ($script:isEditMode) { return } $script:isExpand = $false $script:form.SuspendLayout() $expandFormBtn.Visible = $true $expandFormBtn.BringToFront() $script:form.SetBounds( $script:fold.x, $script:fold.y, $script:fold.w, $script:fold.h ) $script:form.ResumeLayout($false) $editBtnItem.Enabled = $false $editMenuItem.Enabled = $false } # 関数:フォームサイズ変更 展開格納切り替え function Toggle-FormSize { Set-FormSizeSettings $script:isExpand = -not $script:isExpand if ($script:isExpand) { Show-ExpandForm } else { Show-FoldForm } } # 関数:メニューパネル表示切り替え function Toggle-MenuPanelView { $menuPanel.Visible = $script:isMenuPanel if ($script:isMenuPanel) { $script:editMenuBound = $menuPanel.Size.Width } else { $script:editMenuBound = 0 } if ($script:isExpand) { Show-ExpandForm } else { Show-FoldForm } } function Toggle-MoveButton { if ( $script:isMoveBtn ) { $moveBtnItem.Text = "移動する" $expandFormBtn.ForeColor = "LightGreen" $foldFormBtn.ForeColor = "LightGreen" Set-FormSizeSettings Save-MenuTable } else { $moveBtnItem.Text = "移動しない" $expandFormBtn.ForeColor = "Orange" $foldFormBtn.ForeColor = "Orange" } $script:isMoveBtn = -not $script:isMoveBtn } function Toggle-EditMode { $script:isEditMode = $editToggle.Checked if ($script:isEditMode) { $script:isMenuPanel = $true Toggle-MenuPanelView $editToggle.Text = "編集モード: ON" $editToggle.BackColor = 'LightYellow' #$editToggle.ForeColor = 'Black' $addPanelBtn.Visible = $true $resetBtn.Visible = $true $statusText.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#fff") $statusText.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#000") $script:form.SuspendLayout() $script:form.Top = $script:form.Top - 31 $script:form.Left = $script:form.Left - 8 $script:form.FormBorderStyle = 'Sizable' $script:form.ResumeLayout() $col = "#070" if ( -not $script:isMoveBtn ) { Toggle-MoveButton } $editMenuItem.Enabled = $false $moveBtnItem.Enabled = $false } else { $editToggle.Text = "編集モード: OFF" $editToggle.BackColor = 'LightGray' #$s.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#3d3") $addPanelBtn.Visible = $false $resetBtn.Visible = $false $statusText.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") $statusText.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#ccc") $col = "#ddf" $script:form.SuspendLayout() $script:form.Top = $script:form.Top + 31 $script:form.Left = $script:form.Left + 8 $script:form.FormBorderStyle = 'None' $foldFormBtn.BringToFront() $script:form.ResumeLayout() if ( $script:isMoveBtn ) { Toggle-MoveButton } $editMenuItem.Enabled = $true $moveBtnItem.Enabled = $true if ($groupPanels.Keys.Count -eq 1) { $script:isMenuPanel = $false Toggle-MenuPanelView } } foreach ($key in $groupPanels.Keys) { $groupPanels[$key].BackColor = [System.Drawing.ColorTranslator]::FromHtml($col) } } function Set-ButtonGeometory { param($item) $entry = $script:menutable | Where-Object { $_.ボタン番号 -eq $item.Tag.Entry.ボタン番号 } if ($entry) { $entry.Left = $item.Location.X $entry.Top = $item.Location.Y $entry.Width = [Math]::Max($MinButtonWidth, $item.Size.Width) $entry.Height = [Math]::Max($MinButtonHeight, $item.Size.Height) } } function Get-NextButtonNumber{ $maxButtonNumber = $script:menutable | Measure-Object -Property ボタン番号 -Maximum $result = $maxButtonNumber.Maximum + 1 return $result } function Get-MenuTable { $script:menutable = @() # 事前に空の配列として初期化 $script:menutable = Import-Csv -Path $csvPath -Encoding $($csvencode) | ForEach-Object { [PSCustomObject]@{ ボタン番号 = $_.ボタン番号 タイトル = $_.タイトル アイコン = $_.アイコン データ = $_.データ TOP = [int]$_.Top Left = [int]$_.Left Height = [int]$_.Height Width = [int]$_.Width 種類 = $_.種類 グループ = $_.グループ } } $ZeroEntry = $script:menutable | Where-Object { $_.ボタン番号 -eq 0 } if ($null -eq $ZeroEntry) { $script:menutable += [PSCustomObject]@{ ボタン番号 = 0; タイトル = "ウィンドウ"; アイコン = ""; データ = ""; Top = "200"; Left = "100"; Height = "600"; Width = "800"; 種類 = ""; グループ = ""} } $entry = $script:menutable | Where-Object { $_.ボタン番号 -eq 0 } $script:expand.x = [int]$entry.Left $script:expand.y = [int]$entry.Top $script:expand.w = [int]$entry.Width $script:expand.h = [int]$entry.Height $script:fold.x = $script:expand.x + $script:expand.w - $script:fold.w $script:fold.y = $script:expand.y } function Save-MenuTable { # 既存のカスタムオブジェクト配列をソート $script:menutable = $script:menutable | Sort-Object ` @{ Expression = { [int]$_.グループ } }, ` @{ Expression = { [int]$_.ボタン番号 } } # 新しいボタン番号を連番で振り直す $counter = 0 $script:menutable | ForEach-Object { $_.ボタン番号 = $counter; $counter++ } $windowentry = ($script:menutable | Where-Object { $_.ボタン番号 -eq 0 }) $windowentry.Height = $script:expand.h $windowentry.Width = $script:expand.w $windowentry.Top = $script:expand.y $windowentry.Left = $script:expand.x # foreach ($row in $script:menutable) { #     $row.PSObject.Properties.Remove("種類") # } $script:menutable | Export-Csv $csvPath -NoTypeInformation -Encoding $($csvencode) } function Add-MenuTable { param( $number, $title, $data, $icon = "", $top = 0, $left = 0, $height = ($MinButtonHeight), $width = ($MinButtonWidth), $groupId = 1 ) if ($script:menutable -isnot [System.Array]) { $script:menutable = @($script:menutable) # 配列化 } $script:menutable += [PSCustomObject]@{ ボタン番号 = $number タイトル = $title アイコン = $icon データ = $data TOP = [int]$top Left = [int]$left Height = [int]$height Width = [int]$width グループ = [int]$groupId } # Write-Host "NewButton $number $title $icon $data $top $left $height $width" Save-MenuTable Reset-Panels } function Remove-MenuTable { param($number) $existingEntry = $script:menutable | Where-Object { $_.ボタン番号 -eq $number } $result = MsgBox "ボタン番号:$($existingEntry.ボタン番号)`nタイトル:$($existingEntry.タイトル)`n`nボタンを削除しますか?" "確認" "YesNo" if ($result -eq [System.Windows.Forms.DialogResult]::No) { Write-Host "削除キャンセル" return } $script:menutable = $script:menutable | Where-Object { $_.ボタン番号 -ne $number } if ($null -eq $script:menutable) {$script:menutable = @([PSCustomObject]@{ ボタン番号 = 1; タイトル = "メモ帳"; アイコン = ""; データ = "notepad.exe"; Top = "0"; Left = "0"; Height = "64"; Width = "350"})} Save-MenuTable Reset-Panels } function Get-WebTitle { param ([string]$Url) try { $response = Invoke-WebRequest -Uri $Url -UseBasicParsing Write-Host "URL: $Url" # Write-Host "HTTP ステータスコード: $($response.StatusCode)" # ステータスコードが200の場合はタイトルを取得 if ($response.StatusCode -eq 200) { if ($response.Content -match "(.*?)") { $title = $matches[1] } else { $title = $Url } # Write-Host "取得したタイトル: $($title)" return $title } else { throw } } catch { Write-Host "タイトル取得失敗: $Url Error: $_" return $Url # 取得できなかった場合はURLをそのまま返す } } function Add-NewPanel { param([int]$groupId) $flg = ($null -eq $groupId -or $groupId -eq "") if ($flg) { for( $i = 1; $i -le $groupPanels.Keys.Count; $i++){ if ( -not $groupPanels.ContainsKey($i) ) { $newGroupId = $i } } if ($null -eq $newGroupId){ $newGroupId = [int](($groupPanels.Keys | Measure-Object -Maximum).Maximum + 1) } # Write-Host "$newGroupId 構築" } else { $newGroupId = [int]$groupId if ($groupPanels.ContainsKey($groupId)) { # Write-Host "$groupId 再構築の為削除" $groupPanels[$newGroupId].Controls.Clear() $rightPanel.Controls.Remove($groupPanels[$newGroupId]) $groupPanels.Remove($newGroupId) } else { $flg = $true } } $newPanel = New-Object System.Windows.Forms.Panel $newPanel.Dock = 'Fill' $newPanel.Tag = $newGroupId #$newPanel.GetType().GetProperty("DoubleBuffered", $flags).SetValue($newPanel, $true, $null) $newPanel.AllowDrop = $true $rightPanel.Controls.Add($newPanel) $groupPanels[$newGroupId] = $newPanel $newPanel.Add_DragEnter({ param($s, $e) if ($e.Data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop) -or $e.Data.GetDataPresent([Windows.Forms.DataFormats]::Text)) { $e.Effect = [System.Windows.Forms.DragDropEffects]::Copy } else { $e.Effect = [System.Windows.Forms.DragDropEffects]::None } }) $newPanel.Add_DragDrop({ param($s, $e) $pos = $s.PointToClient([System.Windows.Forms.Cursor]::Position) $data = $e.Data if ($data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop)) { $files = $data.GetData([Windows.Forms.DataFormats]::FileDrop) if($files.Count -eq 1) { $file = $files[0] Write-Host "ファイルドロップ: $file" # ファイル処理 $title = [System.IO.Path]::GetFileNameWithoutExtension($file) } else { Write-Host "複数ファイルの位置ずらし方わからん" } } elseif ($data.GetDataPresent([Windows.Forms.DataFormats]::Text)) { $text = $data.GetData([Windows.Forms.DataFormats]::Text) if ($text -match '^https?://') { Write-Host "URLドロップ: $text" # URL処理 $file = $text $title = Get-WebTitle -Url $file } else { Write-Host "テキストドロップ: $text" # 通常テキスト処理 } } else { Write-Host "未知の形式がドロップされました" } # foreach ($key in $groupPanels.Keys) { # Write-Host "$key → $($key.GetType().FullName)" # } Add-MenuTable -number $(Get-NextButtonNumber) -title $title -data $file -top $pos.Y -Left $pos.X -groupId $s.Tag $editToggle.Checked = $true }) # パネルにマウスダウンイベントを追加 $newPanel.Add_MouseDown({ if ( $script:isEditMode ) { [NativeMethods]::ReleaseCapture() | Out-Null [NativeMethods]::SendMessage($form.Handle, $WM_NCLBUTTONDOWN, $HTCAPTION, 0) } }) if ($flg){ $radio = New-Object System.Windows.Forms.RadioButton $radio.Appearance = 'Button' $radio.FlatStyle = 'Standard' $radio.Margin = '0,0,0,0' $radio.Size = New-Object System.Drawing.Size($groupPanel.Width, 100) $radio.Tag = $newGroupId $radio.Text = "グループ $newGroupId" $index = $groupPanel.Controls.Count $groupPanel.Controls.Add($radio) $groupPanel.Controls.SetChildIndex($radio, $groupPanel.Controls.Count - 1) $groupRadios[$newGroupId] = $radio # マウスリーブでタイマー停止 $radio.Add_MouseLeave({ $script:hoverTimer.Stop() $script:currentRadio = $null }) # マウスエンターでタイマー開始 $radio.Add_MouseEnter({ param($s, $e) $script:currentRadio = $s # タイマーTickでクリック発火 $script:hoverTimer.Stop() $script:hoverTimer.Start() }) $radio.Add_CheckedChanged({ param($s, $e) if ($s.Checked) { $s.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#ddf") $s.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#000") $script:currentGroupId = $s.Tag $groupPanels[$s.Tag].BringToFront() if ($groupPanels[$s.Tag].Controls.Count -eq 0) { $statusText.Text = "下のスペースにファイルなどをドロップすると登録されます" } } else { $s.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") $s.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#ccc") $statusText.Text = (Get-Date).ToString("yyyy-MM-dd dddd") } }) $radio.Checked = $true } else { # $groupId } if ($script:isEditMode) { $col = "#070" } else { $col = "#ddf" } $newPanel.BackColor = [System.Drawing.ColorTranslator]::FromHtml($col) $groupRadios[$newGroupId].Checked = $true return $newGroupId } # アイコン生成関数(Labelベース) function Add-LauncherPanel($item, $targetPanel) { $item.Width = [Math]::Max($MinButtonWidth, $item.Width) $item.Height = [Math]::Max($MinButtonHeight, $item.Height) $panel = New-Object System.Windows.Forms.Panel $panel.Size = New-Object System.Drawing.Size($item.Width, $item.Height) $panel.Location = New-Object System.Drawing.Point($item.Left, $item.Top) $panel.Font = New-Object System.Drawing.Font("メイリオ",24) $panel.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") $panel.GetType().GetProperty("DoubleBuffered", $flags).SetValue($panel, $true, $null) #$panel.BorderStyle = 'FixedSingle' $panel.BorderStyle = 'None' # アイコン設定 $iconpath = "" if( $item.アイコン -ne "") { $iconpath = "$($item.アイコン -replace "^\.\\", "$PSScriptRoot\")" } elseif ($item.種類 -eq 'URL') { $uri = [System.Uri]::new($item.データ) $iconpath = "$PSScriptRoot\icon\$($uri.Host).png" } elseif ($item.種類 -eq '開く') { $iconpath = "$PSScriptRoot\icon\$([System.IO.Path]::GetExtension($item.データ)).png" } else { $iconpath = "$PSScriptRoot\icon\$([System.IO.Path]::GetFileNameWithoutExtension($($item.データ))).png" } if ($null -eq $iconpath -or $iconpath -eq "" -or $iconpath -eq ".png") { $iconpath = "" } $existIconFile = Test-Path -Path $iconpath # Write-Host $iconpath, $existIconFile, $item.種類.ToString() if ($existIconFile) { try { $icon = [System.Drawing.Image]::FromFile($iconpath) } catch { Write-Host "画像読み込み失敗: $iconpath" } } else { switch ($item.種類) { 'URL' { $uri = [System.Uri]::new($item.データ) $iconpath = "$PSScriptRoot\icon\$($uri.Host).png" if (Test-Path $iconpath) { $icon = [System.Drawing.Image]::FromFile($iconpath) } else { $faviconUrl = "$($uri.Scheme)://$($uri.Host)/favicon.ico" try { $response = Invoke-WebRequest -Uri $faviconUrl -Headers @{ "User-Agent" = "Mozilla/5.0" } -Method Get -ErrorAction Stop $ms = New-Object System.IO.MemoryStream $ms.Write($response.Content, 0, $response.Content.Length) $ms.Position = 0 $icon = [System.Drawing.Image]::FromStream($ms) } catch { Write-Host "Favicon取得失敗: $faviconUrl" $file = (Get-Command $BrowserPath).Source $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($file) $icon = $icon.ToBitmap() } } } 'フォルダ' { $icon = $script:explorericon $icon = $icon.ToBitmap() $iconpath = "" } 'PowerShell' { $icon = $script:powershellicon $icon = $icon.ToBitmap() $iconpath = "" } '実行' { try { $file = (Get-Command $item.データ -ErrorAction Stop).Source Write-Host $file $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($file) $icon = $icon.ToBitmap() } catch { Write-Host "実行アイコン取得失敗: $($item.データ)" } } default { $ext = [System.IO.Path]::GetExtension($item.データ) if ($ext -ne "") { $dummyFile = "$env:TEMP\dummy$ext" New-Item -Path $dummyFile -ItemType File -Force | Out-Null $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($dummyFile) $icon = $icon.ToBitmap() Remove-Item $dummyFile -Force $iconpath = "$PSScriptRoot\icon\$($ext).png" } } } } if ($null -ne $icon -and $null -ne $iconpath -and $iconpath -ne "" ) { if(-not (Test-Path -Path $iconpath)) { try{ #Write-Host $iconpath, $item.種類.ToString(), $existIconFile $icon.Save($iconpath, [System.Drawing.Imaging.ImageFormat]::Png) } catch { Write-Host "アイコンファイルの保存に失敗" } } } $titlelength = Get-HalfWidthLength -text $item.タイトル $fontsize = if ($($titlelength) -le 10) { $FontLevel["L"] } elseif ($($titlelength) -le 16) { $FontLevel["M"] } elseif ($($titlelength) -le 20) { $FontLevel["S"] } else { $FontLevel["SS"] } $btnFont = New-Object System.Drawing.Font("Meyrio",$fontsize,[System.Drawing.FontStyle]::Bold) $btnColor = New-Object Drawing.SolidBrush ([Drawing.ColorTranslator]::FromHtml($colorMap[$item.種類])) $panel.Tag = @{ Entry = $item Icon = $icon Font = $btnFont Color = $btnColor } $panel.Add_Paint({ param($s, $e) $entry = $s.Tag.Entry $icon = $s.Tag.Icon $btnFont = $s.Tag.Font $btnColor = $s.Tag.Color $g = $e.Graphics $g.SmoothingMode = 'AntiAlias' $r = $s.ClientRectangle $radius = 12 $path = [System.Drawing.Drawing2D.GraphicsPath]::new() $path.AddArc($r.X, $r.Y, $radius, $radius, 180, 90) $path.AddArc($r.Right - $radius, $r.Y, $radius, $radius, 270, 90) $path.AddArc($r.Right - $radius, $r.Bottom - $radius, $radius, $radius, 0, 90) $path.AddArc($r.X, $r.Bottom - $radius, $radius, $radius, 90, 90) $path.CloseFigure() # $g.FillPath([System.Drawing.Brushes]::LightSteelBlue, $path) $pen = New-Object System.Drawing.Pen([System.Drawing.Color]::Silver, 3) $s.Region = New-Object System.Drawing.Region($path) $g.DrawPath($pen, $path) if ($icon) { $y = ($s.Height - $iconSize) / 2 + 1 $g.DrawImage($icon, 3, $y, $iconSize, $iconSize) $textleft = $iconSize + 1 } else { $textleft = 0 } $y = ($s.Height - $btnFont.Height) / 2 $g.DrawString($entry.タイトル, $btnFont, $btnColor, $textleft, $y) }) # コンテキストメニュー作成 $btnPanelContext = New-Object System.Windows.Forms.ContextMenuStrip # フォルダを開くメニュー項目 $parentItem = New-Object System.Windows.Forms.ToolStripMenuItem $parentItem.Text = "フォルダを開く" $parentItem.Tag = $item # セパレータ作成 $sep1 = New-Object System.Windows.Forms.ToolStripSeparator # グループ変更メニュー項目 $changeGroupItem = New-Object System.Windows.Forms.ToolStripMenuItem $changeGroupItem.Text = "グループ変更" $changeGroupItem.Tag = $item # ボタン削除メニュー項目 $delBtnPanelItem = New-Object System.Windows.Forms.ToolStripMenuItem $delBtnPanelItem.Text = "ボタンを削除" $delBtnPanelItem.Tag = $item # メニューに追加 $btnPanelContext.Items.AddRange(@( $parentItem, $sep1, $changeGroupItem, $delBtnPanelItem )) # パネルにメニューを割り当て $Panel.ContextMenuStrip = $btnPanelContext # コンテキストメニューイベント $btnPanelContext.Add_Opening({ param($s, $e) # 表示直前の処理 $items = $s.Items $changeGroupItem = $items | Where-Object { $_.Text -eq "グループ変更" } $delBtnPanelItem = $items | Where-Object { $_.Text -eq "ボタンを削除" } $changeGroupItem.Enabled = $script:isEditMode $delBtnPanelItem.Enabled = $script:isEditMode }) $parentItem.Add_Click({ param($s, $e) Open-ParentFolder $s.Tag.データ }) $changeGroupItem.Add_Click({ param($s, $e) $selectedGroup = Show-GroupSelector -groupPanels $groupPanels if ($selectedGroup -ne $null) { Write-Host "選択されたグループID: $selectedGroup" $entry = $script:menutable | Where-Object { $_.ボタン番号 -eq $s.Tag.ボタン番号 } $entry.グループ = [int]$selectedGroup $groupRadios[$entry.グループ].Checked = $true Save-MenuTable Reset-Panels } }) $delBtnPanelItem.Add_Click({ param($s, $e) Remove-MenuTable $s.Tag.ボタン番号 }) # イベント処理(パネルに対して) $panel.Add_Click({ param($s, $e) if (-not $script:isEditMode) { Show-FoldForm $entry = $s.Tag.Entry $type = $entry.種類 $param = $entry.データ switch ($type) { 'URL' { & $BrowserPath $param } 'PowerShell' { Start-Process "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -NoProfile -File `"$param`"" } '実行' { & $param } '実行2' { Invoke-Item $param } 'フォルダ' { & "explorer.exe" $param } '開く' { Invoke-Item $param } default { Write-Host "未定義の種類: `"$type`"" } } } }) $Script:isDragging = $false $Script:isResizing = $false $Script:startMouse = [System.Drawing.Point]::Empty $Script:offset = [System.Drawing.Point]::Empty $Script:threshold = 20 $panel.Add_MouseHover({ param($s, $e) $entry = $s.Tag.Entry if ($script:isEditMode) { $tooltext = "x:{0,4} y:{1,4} w:{2,4} h:{3,4} $($s.Tag.Entry.データ)" -f $s.Left ,$s.Top, $s.Width, $s.Height $tooltip.Show($tooltext, $s, 0, $s.Height, 5000) } }) $panel.Add_MouseEnter({ param($s, $e) $statusText.Text = $s.Tag.Entry.タイトル $statusText.Tag = $s.Tag.Entry.ボタン番号 if(-not $script:isEditMode){ $s.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#4a4d51") $s.Cursor = 'Hand' } else { $statusText.Focus() $statusText.SelectAll() } }) $panel.Add_MouseLeave({ param($s, $e) $statusText.Text = "" $statusText.Tag = "" $s.Cursor = $null if(-not $script:isEditMode){ $s.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") } }) $panel.Add_MouseDown({ param($s, $e) if ($script:isEditMode) { if ($e.Button -eq [System.Windows.Forms.MouseButtons]::Right) { return } $Script:startMouse = $script:form.PointToClient([System.Windows.Forms.Control]::MousePosition) $Script:offset = $e.Location if ($e.X -gt ($s.Width - $Script:threshold) -and $e.Y -gt ($s.Height - $Script:threshold)) { $Script:isResizing = $true } else { $Script:isDragging = $true } $s.Capture = $true } }) $panel.Add_MouseMove({ param($s, $e) $mousePos = $script:form.PointToClient([System.Windows.Forms.Control]::MousePosition) if ($script:isEditMode) { # つかむ前でもカーソルを変える if (-not $Script:isDragging -and -not $Script:isResizing) { if ($e.X -ge $s.Width - 10 -and $e.Y -ge $s.Height - 10) { $s.Cursor = 'Cross' # リサイズ予告 } else { $s.Cursor = 'SizeAll' # ドラッグ予告 } } } if ($script:isEditMode -and ($Script:isDragging -or $Script:isResizing)) { $offsetX = $mousePos.X - $Script:startMouse.X $offsetY = $mousePos.Y - $Script:startMouse.Y if ($Script:isDragging) { $s.Left += $offsetX if ($s.Left -lt 0) {$s.Left = 0} $s.Top += $offsetY if ($s.Top -lt 0) {$s.Top = 0} $s.BackColor = 'Pink' } if ($Script:isResizing) { $s.Width = [Math]::Max($MinButtonWidth, $s.Width + $offsetX) $s.Height = [Math]::Max($MinButtonHeight, $s.Height + $offsetY) $s.Invalidate() $s.BackColor = 'DarkBlue' } $Script:startMouse = $mousePos $statusText.Text = "Left:$($s.Left) Top:$($s.Top) Width:$($s.Width) Height:$($s.Height)" } }) $panel.Add_MouseUp({ param($s, $e) Write-Host $Script:isDragging , $Script:isResizing if ($Script:isDragging -or $Script:isResizing) { Write-Host $s.Tag.Entry.データ Set-ButtonGeometory -item $s } $Script:isDragging = $false $Script:isResizing = $false $s.Capture = $false $s.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") #$s.Cursor = 'Hand' }) # Write-Host "targetPanel: $($targetPanel -ne $null)" # Write-Host "panel: $($panel -ne $null)" # 子コントロールを追加 $targetPanel.Controls.Add($panel) } # アイコンをパネルに追加 function Add-Button { for ($i = 0; $i -lt $script:menutable.Count; $i++) { $item = $script:menutable[$i] $btnId = [int]$item.ボタン番号 if ($btnId -eq 0) { continue } if ($null -eq $item.グループ -or $item.グループ -eq "" -or $item.グループ -eq 0 -or $item.グループ -eq "0") { $groupId = [int][math]::Ceiling($btnId / $btnCount) $script:menutable[$i].グループ = $groupId # ← ここで元データに反映 } else { $groupId = [int]$item.グループ } if ($item.種類 -eq "") { $script:menutable[$i].種類 = Get-EntryType -data $item.データ } $script:menutable[$i].タイトル = $item.タイトル -replace "(`r`n|`r|`n)", "" if (-not $groupPanels.ContainsKey($groupId)) { $groupId = [int]$groupId Add-NewPanel $groupId } Add-LauncherPanel $item $groupPanels[$groupId] } } function Reset-Panels { Write-Host "reset" $currentId = $script:currentGroupId Get-MenuTable $deletePanels = @($groupPanels.Keys) $script:form.SuspendLayout() ForEach ($groupId in $deletePanels) { $groupId = [int]$groupId Add-NewPanel $groupId } Add-Button $groupPanels[$currentId].BringToFront() $groupRadios[$currentId].Checked = $true $script:form.ResumeLayout() } # データ種類判定 function Get-EntryType { param ( [string]$data ) if ($data -match '"([^"]+)"') { $data = $matches[1] } if ($data -eq "") { return '' } elseif ($data -match '(?i)^(?:https?|file)://\S+') { return 'URL' } elseif ($data -like '*.ps1') { return 'PowerShell' } elseif ($data -like '*.exe') { return '実行' } elseif ($data -match '\.cmd$|\.bat$') { return '実行2' } elseif ((Get-Item $data).PSIsContainer) { return 'フォルダ' } else { return '開く' } } # 文字数カウンタ function Get-HalfWidthLength { param($text) return ($text -replace "[^\u0020-\u007E]", "11").Length } # フォーム作成 $script:form = New-Object System.Windows.Forms.Form $script:form.Text = "ランチャー" $script:form.Size = New-Object Drawing.Size($script:menutable[0].Width,$script:menutable[0].Height) $script:form.StartPosition = "Manual" # 手動指定を有効化 $script:form.Location = New-Object Drawing.Point($script:menutable[0].Left, $script:menutable[0].Top) $script:form.FormBorderStyle = 'None' $script:form.GetType().GetProperty("DoubleBuffered", $flags).SetValue($script:form, $true, $null) # 展開表示ボタン $expandFormBtn = New-Object System.Windows.Forms.Button $expandFormBtn.Text = "◆" $expandFormBtn.Font = New-Object System.Drawing.Font("Meyrio",24,[System.Drawing.FontStyle]::Bold) $expandFormBtn.TextAlign = 'BottomCenter' #$expandFormBtn.FlatStyle = 'Standard' $expandFormBtn.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") $expandFormBtn.ForeColor = "LightGreen" $expandFormBtn.Size = New-Object Drawing.Size($script:fold.w, $script:fold.h) $expandFormBtn.Location = '0,0' $expandFormBtn.Visible = $true $script:form.Controls.Add($expandFormBtn) # 右パネル $rightPanel = New-Object System.Windows.Forms.Panel $rightPanel.Dock = 'Fill' $script:form.Controls.Add($rightPanel) # 表示領域 $statusPanel = New-Object System.Windows.Forms.Panel $statusPanel.Dock = 'Top' $statusPanel.Height = $script:fold.h $statusPanel.Padding = '0,0,0,0' $statusPanel.BorderStyle = 'None' $rightPanel.Controls.Add($statusPanel) # 格納表示ボタン $foldFormBtn = New-Object System.Windows.Forms.Button $foldFormBtn.Text = "◇" $foldFormBtn.Font = New-Object System.Drawing.Font("Meyrio",24,[System.Drawing.FontStyle]::Bold) $foldFormBtn.TextAlign = 'BottomCenter' $foldFormBtn.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") $foldFormBtn.ForeColor = "LightGreen" $foldFormBtn.Size = New-Object Drawing.Size($script:fold.w, $script:fold.h) $foldFormBtn.Dock = 'Right' $statusPanel.Controls.Add($foldFormBtn) # ステータス表示テキストボックス $statusText = New-Object System.Windows.Forms.TextBox $statusText.Dock = 'Fill' #$statusText.BorderStyle = 'None' $statusText.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") $statusText.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#ccc") $statusText.Height = $script:fold.h $statusText.Font = 'Meyrio,29' $statusPanel.Controls.Add($statusText) # 左メニューパネル $menuPanel = New-Object System.Windows.Forms.Panel $menuPanel.Size = '120,700' $menuPanel.Dock = 'Left' $script:form.Controls.Add($menuPanel) # グループ選択ボタン用パネル(上寄せ) $groupPanel = New-Object Windows.Forms.FlowLayoutPanel $groupPanel.Dock = 'Fill' $groupPanel.FlowDirection = 'TopDown' $groupPanel.WrapContents = $false $groupPanel.AutoScroll = $true $groupPanel.Padding = '0,0,0,0' $groupPanel.Font = New-Object System.Drawing.Font("Meyrio", 12) $groupPanel.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") $groupPanel.ForeColor = [System.Drawing.ColorTranslator]::FromHtml("#ccc") $menuPanel.Controls.Add($groupPanel) # 編集・追加ボタン用パネル(下寄せ) $editPanel = New-Object Windows.Forms.Panel $editPanel.Dock = 'Bottom' $editPanel.BackColor = [System.Drawing.ColorTranslator]::FromHtml("#333") $menuPanel.Controls.Add($editPanel) # パネル追加ボタン $addPanelBtn = New-Object System.Windows.Forms.Button $addPanelBtn.Text = "パネル追加" $addPanelBtn.Dock = 'Bottom' $addPanelBtn.BackColor = 'LightGreen' $addPanelBtn.Visible = $script:isEditMode $editPanel.Controls.Add($addPanelBtn) # パネル再構築ボタン $resetBtn = New-Object System.Windows.Forms.Button $resetBtn.Text = "リセット" $resetBtn.Dock = 'Bottom' $resetBtn.BackColor = 'LightBlue' $resetBtn.Visible = $script:isEditMode $editPanel.Controls.Add($resetBtn) # 編集モードトグルボタン $editToggle = New-Object System.Windows.Forms.CheckBox $editToggle.Appearance = 'Button' $editToggle.FlatStyle = 'Standard' $editToggle.Text = "編集モード: OFF" $editToggle.Checked = $script:isEditMode $editToggle.Dock = 'Bottom' $editToggle.BackColor = 'LightGray' $editPanel.Controls.Add($editToggle) # コンテキストメニュー作成 $editPanelContext = New-Object System.Windows.Forms.ContextMenuStrip # 展開ボタン移動モードメニュー項目 $moveBtnItem = New-Object System.Windows.Forms.ToolStripMenuItem $moveBtnItem.Text = "移動" # セパレータ作成 $separator1 = New-Object System.Windows.Forms.ToolStripSeparator # 編集モードメニュー項目 $editBtnItem = New-Object System.Windows.Forms.ToolStripMenuItem $editBtnItem.Text = "編集" # 編集モードメニュー項目 $editMenuItem = New-Object System.Windows.Forms.ToolStripMenuItem $editMenuItem.Text = "編集メニュー" # セパレータ作成 $separator2 = New-Object System.Windows.Forms.ToolStripSeparator # スクリプトフォルダ開くメニュー項目 $openRootItem = New-Object System.Windows.Forms.ToolStripMenuItem $openRootItem.Text = "フォルダを開く" # 終了メニュー項目 $exitItem = New-Object System.Windows.Forms.ToolStripMenuItem $exitItem.Text = "終了" # 再起動メニュー項目 $restartItem = New-Object System.Windows.Forms.ToolStripMenuItem $restartItem.Text = "再起動" # メニューに追加 $editPanelContext.Items.AddRange(@( $openRootItem, $moveBtnItem, $separator1, $editBtnItem, $editMenuItem, $separator2, $exitItem, $restartItem )) # パネルにメニューを割り当て $editPanel.ContextMenuStrip = $editPanelContext $expandFormBtn.ContextMenuStrip = $editPanelContext $foldFormBtn.ContextMenuStrip = $editPanelContext ########## #イベント $script:form.Add_Resize({ Set-FormSizeSettings }) # ドラッグ移動用イベント $groupPanel.Add_MouseDown({ [NativeMethods]::ReleaseCapture() | Out-Null [NativeMethods]::SendMessage($form.Handle, $WM_NCLBUTTONDOWN, $HTCAPTION, 0) }) $editPanel.Add_MouseDown({ [NativeMethods]::ReleaseCapture() | Out-Null [NativeMethods]::SendMessage($form.Handle, $WM_NCLBUTTONDOWN, $HTCAPTION, 0) }) $expandFormBtn.Add_MouseDown({ if ($script:isMoveBtn) { [NativeMethods]::ReleaseCapture() | Out-Null [NativeMethods]::SendMessage($form.Handle, $WM_NCLBUTTONDOWN, $HTCAPTION, 0) } }) $expandFormBtn.Add_Click({ if ($script:isEditMode) { return } Toggle-FormSize }) $foldFormBtn.Add_Click({ if ($script:isEditMode) { return } Toggle-FormSize }) $foldFormBtn.Add_MouseDown({ if ($script:isMoveBtn) { [NativeMethods]::ReleaseCapture() | Out-Null [NativeMethods]::SendMessage($form.Handle, $WM_NCLBUTTONDOWN, $HTCAPTION, 0) } }) $statusText.Add_KeyDown({ param($s, $e) if ($e.KeyCode -eq [System.Windows.Forms.Keys]::Enter) { if ($s.Tag -ne "" -and $s.Text -ne "" -and $script:isEditMode){ $entry = $script:menutable | Where-Object { $_.ボタン番号 -eq $s.Tag } $entry.タイトル = $s.Text Save-MenuTable Reset-Panels } } }) $addPanelBtn.Add_Click({ Add-NewPanel }) $resetBtn.Add_Click({ Save-MenuTable Reset-Panels }) $editToggle.Add_CheckedChanged({ Save-MenuTable Toggle-EditMode }) # コンテキストメニューイベント $moveBtnItem.Add_Click({ Toggle-MoveButton }) $editBtnItem.Add_Click({ $editToggle.Checked = -not $editToggle.Checked }) $editMenuItem.Add_Click({ $script:isMenuPanel = -not $script:isMenuPanel Toggle-MenuPanelView }) $openRootItem.Add_Click({ Show-FoldForm & explorer.exe "$PSScriptRoot" }) $exitItem.Add_Click({ $form.Close() }) $restartItem.Add_Click({ # 再起動処理(フォームを閉じて再度起動) # Start-Process powershell -ArgumentList "-ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$PSCommandPath`"" Start-Process conhost.exe -ArgumentList "powershell -ExecutionPolicy Bypass -NoProfile -WindowStyle Hidden -File `"$PSCommandPath`"" $script:form.Close() }) $script:form.Add_Paint({ param($s, $e) if ( -not $script:isEditMode ) { $g = $s.Graphics $rect = $script:form.ClientRectangle [System.Windows.Forms.ControlPaint]::DrawBorder( $g, $rect, [System.Drawing.Color]::Red, 3, 'Solid', [System.Drawing.Color]::Red, 3, 'Solid', [System.Drawing.Color]::Red, 3, 'Solid', [System.Drawing.Color]::Red, 3, 'Solid' ) } }) $script:form.Add_Load({ # 設定読込 Get-MenuTable # アイコンパネル生成 Add-Button $script:form.Location = New-Object Drawing.Point($script:fold.x, $script:fold.y) $script:form.Size = New-Object Drawing.Size($script:fold.w, $script:fold.h) $menuPanel.Visible = $false $editBtnItem.Enabled = $false $editMenuItem.Enabled = $false }) $script:form.Add_Shown({ $groupRadios[1].Checked = $true $expandFormBtn.BringToFront() if (-not $script:isMenuPanel -and $groupPanels.Keys.Count -gt 1) { $script:isMenuPanel = $true Toggle-MenuPanelView } }) $script:form.TopMost = $true $script:form.ShowDialog()