From f048be792c879d63314ee68f14cbd46c2a01627d Mon Sep 17 00:00:00 2001 From: yu <191972707@qq.com> Date: Fri, 30 Oct 2026 10:33:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=A1=8C=E9=9D=A2=E6=8B=BE=E5=8F=96=20?= =?UTF-8?q?v3=E2=80=94=E2=80=94=E9=A1=B6=E9=83=A8=E5=B1=85=E4=B8=AD?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E6=9D=A1=E3=80=81=E9=AB=98=E4=BA=AE=E6=A1=86?= =?UTF-8?q?=E8=B7=9F=E9=9A=8F=E3=80=81=E4=B8=A4=E9=98=B6=E6=AE=B5=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=EF=BC=88=E9=94=81=E5=AE=9A/=E5=BE=AE=E8=B0=83?= =?UTF-8?q?=EF=BC=89=EF=BC=8C=E6=98=BE=E5=BC=8F=20UTF8=20BOM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/desktop-picker.ps1 | 134 ++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 56 deletions(-) diff --git a/resources/desktop-picker.ps1 b/resources/desktop-picker.ps1 index cafe81b..c88696e 100644 --- a/resources/desktop-picker.ps1 +++ b/resources/desktop-picker.ps1 @@ -15,21 +15,26 @@ if ($Mode -eq "windows") { exit 0 } -# ---- 信息窗 ---- -$form = New-Object System.Windows.Forms.Form -$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None -$form.TopMost = $true -$form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual -$form.Location = New-Object System.Drawing.Point(20, 20) -$form.Size = New-Object System.Drawing.Size(480, 110) -$form.BackColor = [System.Drawing.Color]::FromArgb(20, 60, 120) -$label = New-Object System.Windows.Forms.Label -$label.Dock = [System.Windows.Forms.DockStyle]::Fill -$label.ForeColor = [System.Drawing.Color]::White -$label.Font = New-Object System.Drawing.Font("Microsoft YaHei", 10) -$form.Controls.Add($label) +# ===== 信息条(屏幕顶部居中,显眼) ===== +$info = New-Object System.Windows.Forms.Form +$info.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None +$info.TopMost = $true +$info.ShowInTaskbar = $false +$info.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual +$sw = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width +$info.Location = New-Object System.Drawing.Point(([int]($sw / 2) - 280), 10) +$info.Size = New-Object System.Drawing.Size(560, 64) +$info.BackColor = [System.Drawing.Color]::FromArgb(15, 40, 90) +$info.Opacity = 0.92 +$infoLabel = New-Object System.Windows.Forms.Label +$infoLabel.Dock = [System.Windows.Forms.DockStyle]::Fill +$infoLabel.ForeColor = [System.Drawing.Color]::White +$infoLabel.Font = New-Object System.Drawing.Font("Microsoft YaHei", 11) +$infoLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter +$infoLabel.Text = "移动鼠标到目标元素上" +$info.Controls.Add($infoLabel) -# ---- 高亮窗(半透明红块) ---- +# ===== 高亮框(半透明红块跟随) ===== $hl = New-Object System.Windows.Forms.Form $hl.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None $hl.TopMost = $true @@ -38,25 +43,45 @@ $hl.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual $hl.BackColor = [System.Drawing.Color]::Red $hl.Opacity = 0.35 -# ---- 状态 ---- +function Set-HighlightBox([int]$x, [int]$y, [int]$w, [int]$h) { + $hl.Location = New-Object System.Drawing.Point(($x - 2), ($y - 2)) + $hl.Size = New-Object System.Drawing.Size(($w + 4), ($h + 4)) + $hl.Show() +} + +# ===== 下钻到包含点的最小后代 ===== +function Get-DeepestAtPoint([double]$px, [double]$py) { + $point = New-Object System.Windows.Automation.Point($px, $py) + $cur = [System.Windows.Automation.AutomationElement]::FromPoint($point) + if (-not $cur) { return $null } + for ($depth = 0; $depth -lt 25; $depth++) { + $children = $cur.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition) + $next = $null + $nextArea = [double]::MaxValue + foreach ($c in $children) { + try { + $r = $c.Current.BoundingRectangle + if ($r.Width -gt 1 -and $r.Height -gt 1 -and $px -ge $r.X -and $px -le ($r.X + $r.Width) -and $py -ge $r.Y -and $py -le ($r.Y + $r.Height)) { + $area = $r.Width * $r.Height + if ($area -lt $nextArea) { $next = $c; $nextArea = $area } + } + } catch {} + } + if (-not $next) { break } + $cur = $next + } + return $cur +} + +$info.Show() +$hl.Show() + $stage = "hover" $chain = New-Object System.Collections.ArrayList $selIdx = -1 -$prevPageUp = $false -$prevPageDown = $false $prevSpace = $false - -function Show-Element($el) { - if ($null -eq $el) { $script:hl.Hide(); return } - $r = $el.Current.BoundingRectangle - if ($r.Width -le 0 -or $r.Height -le 0) { $script:hl.Hide(); return } - $script:hl.Location = New-Object System.Drawing.Point([int]($r.X - 2), [int]($r.Y - 2)) - $script:hl.Size = New-Object System.Drawing.Size([int]($r.Width + 4), [int]($r.Height + 4)) - $script:hl.Show() -} - -$form.Show() -$hl.Show() +$prevPgUp = $false +$prevPgDn = $false while ($true) { $space = ([RpaPick.PickW32]::GetAsyncKeyState(32) -band 0x8000) -ne 0 @@ -69,58 +94,55 @@ while ($true) { if ($stage -eq "hover") { $ptX = [System.Windows.Forms.Cursor]::Position.X $ptY = [System.Windows.Forms.Cursor]::Position.Y - $point = New-Object System.Windows.Automation.Point($ptX, $ptY) - $el = $null - try { $el = [System.Windows.Automation.AutomationElement]::FromPoint($point) } catch {} + $el = Get-DeepestAtPoint $ptX $ptY if ($el) { $c = $el.Current $ctrlName = $c.ControlType.ProgrammaticName.Replace("ControlType.", "") - $label.Text = "名称: " + $c.Name + [Environment]::NewLine + "类型: " + $ctrlName + [Environment]::NewLine + "空格=锁定 Esc=取消" - Show-Element $el + Set-HighlightBox $ptX $ptY 1 1 + $r = $c.Current.BoundingRectangle + if ($r.Width -gt 0) { Set-HighlightBox ([int]$r.X) ([int]$r.Y) ([int]$r.Width) ([int]$r.Height) } + $infoLabel.Text = "名称: " + $c.Name + " 类型: " + $ctrlName + " [空格]锁定 [Esc]取消" } if ($space -and -not $prevSpace -and $el) { - # 进入微调:收集从根到命中的元素链 + $stage = "locked" + $chain = New-Object System.Collections.ArrayList $walker = [System.Windows.Automation.TreeWalker]::ControlViewWalker - $chain.Clear() $cur = $el while ($cur -and -not $cur.Equals([System.Windows.Automation.AutomationElement]::RootElement)) { - $cc = $cur.Current - $rr = $cc.BoundingRectangle - [void]$chain.Insert(0, @{ el = $cur; name = [string]$cc.Name; automationId = [string]$cc.AutomationId; controlType = $cc.ControlType.ProgrammaticName.Replace("ControlType.", ""); x = [int]$rr.X; y = [int]$rr.Y; w = [int]$rr.Width; h = [int]$rr.Height }) + $c = $cur.Current + $r = $c.BoundingRectangle + [void]$chain.Insert(0, @{ el = $cur; name = [string]$c.Name; automationId = [string]$c.AutomationId; controlType = $c.ControlType.ProgrammaticName.Replace("ControlType.", ""); x = [int]$r.X; y = [int]$r.Y; w = [int]$r.Width; h = [int]$r.Height }) $cur = $walker.GetParent($cur) } $selIdx = $chain.Count - 1 - $stage = "adjust" - $label.Text = "微调模式:PageUp/PageDown 切换层级,空格确认,Esc 取消" + $infoLabel.Text = "已锁定 [" + ($selIdx + 1) + "/" + $chain.Count + "] [PageUp/PageDown]调层级 [空格]确认回填 [Esc]取消" } } - if ($stage -eq "adjust") { - if ($pgUp -and -not $prevPageUp -and $selIdx -gt 0) { $selIdx -= 1 } - if ($pgDn -and -not $prevPageDown -and $selIdx -lt ($chain.Count - 1)) { $selIdx += 1 } + if ($stage -eq "locked") { $item = $chain[$selIdx] if ($item) { - $label.Text = "层级 " + ($selIdx + 1) + "/" + $chain.Count + " 名称: " + $item.name + [Environment]::NewLine + "类型: " + $item.controlType + [Environment]::NewLine + "PageUp/PageDown=切层级 空格=确认 Esc=取消" - $hl.Location = New-Object System.Drawing.Point($item.x - 2, $item.y - 2) - $hl.Size = New-Object System.Drawing.Size($item.w + 4, $item.h + 4) - $hl.Show() + Set-HighlightBox $item.x $item.y $item.w $item.h + $infoLabel.Text = "层级 " + ($selIdx + 1) + "/" + $chain.Count + " 名称: " + $item.name + " 类型: " + $item.controlType + " [PageUp/PageDown]调层级 [空格]确认 [Esc]取消" } + if ($pgUp -and -not $prevPgUp -and $selIdx -gt 0) { $selIdx -= 1 } + if ($pgDn -and -not $prevPgDn -and $selIdx -lt ($chain.Count - 1)) { $selIdx += 1 } if ($space -and -not $prevSpace) { $item = $chain[$selIdx] - # 窗口名 = 链中第一个带窗口句柄的层 - $rootName = "" - foreach ($it in $chain) { if ($it.name) { $rootName = $it.name; break } } - $result = @{ selected = $selIdx; window = $rootName; name = $item.name; automationId = $item.automationId; controlType = $item.controlType; x = $item.x; y = $item.y; w = $item.w; h = $item.h } + $rootTitle = "" + foreach ($it in $chain) { if ($it.name) { $rootTitle = $it.name; break } } + $result = @{ selected = $selIdx; window = $rootTitle; name = $item.name; automationId = $item.automationId; controlType = $item.controlType; x = $item.x; y = $item.y; w = $item.w; h = $item.h } break } } - $prevPageUp = $pgUp - $prevPageDown = $pgDn $prevSpace = $space + $prevPgUp = $pgUp + $prevPgDn = $pgDn Start-Sleep -Milliseconds 60 } -$form.Close() + +$info.Close() $hl.Close() -if ($result) { Write-Output (ConvertTo-Json $result -Compress) } else { Write-Output (ConvertTo-Json @{ cancelled = $true } -Compress) } +if ($result) { Write-Output (ConvertTo-Json $result -Compress) } else { Write-Output (ConvertTo-Json @{ cancelled = $true } -Compress) } \ No newline at end of file