← Commit history

v0.9.86: activate the selected tree row (Enter to tree hwnd + PostMessage dblclick)

John Lauer ·1feede1a4d ·2mo ago ·parent 875b48f
5 files changed +59−15
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.9.85+0.9.86
adom-bridge-kicad-manifest.json+5−5
@@ -1,15 +1,15 @@ {   "manifest_version": 1,   "name": "kicad",-  "version": "0.9.85",-  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.85/adom-bridge-kicad-v0.9.85.zip",-  "sha256": "3fd994fd1b2a8c37f6c404715c5fbd8643e77f26dda44e5d525dc896eb97aba7",-  "size": 378108,+  "version": "0.9.86",+  "url": "https://wiki.adom.inc/download/adom/kicad-bridge/0.9.86/adom-bridge-kicad-v0.9.86.zip",+  "sha256": "79971a758f954299d7a0df3db3df4ad7809b30e979df5bdca3522c0ac19c0d16",+  "size": 378689,   "verbPrefixes": [     "kicad_"   ],   "healthEndpoint": "/status",-  "released_at": "2026-08-14T15:39:13Z",+  "released_at": "2026-08-14T15:41:37Z",   "hero": "https://wiki.adom.inc/blob/app/kicad-bridge/kicad-hero.png",   "languages": [     "Python",
adom-bridge-kicad-v0.9.86.zipadded
⋯ 1 unchanged line ⋯
bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "kicad",   "displayName": "KiCad EDA",-  "version": "0.9.85",+  "version": "0.9.86",   "description": "Reverse bridge for KiCad \u2014 board/schematic introspection, lint via kicad-cli, plugin install, multi-instance probe, in-process DRC.",   "homepage": "https://wiki.adom.inc/adom/adom-bridge",   "author": "Adom Inc.",
handlers/open_symbol_editor.py+52−8
@@ -205,18 +205,62 @@ def _background_navigate_win32(sym_hwnd: int, filter_text: str, verify: str) ->         return False     time.sleep(1.4)  # LIB_TREE search debounce runs on the UI thread (~200-500ms) + tree rebuild     VK_DOWN = 0x28-    # KiCad's filter box forwards navigation keys to the tree: Down selects the-    # first filtered match, Enter activates (= load into canvas). Same sequence-    # a human types; all posted to the Edit hwnd, zero focus needed.+    # Down THROUGH the filter box: KiCad forwards nav keys to the tree, which+    # selects the first filtered match (verified live: Device:R row highlights).     user32.PostMessageW(edit, WM_KEYDOWN, VK_DOWN, 0)     user32.PostMessageW(edit, WM_KEYUP, VK_DOWN, 0)     time.sleep(0.5)-    user32.PostMessageW(edit, WM_KEYDOWN, VK_RETURN, 0)-    user32.PostMessageW(edit, WM_KEYUP, VK_RETURN, 0)-    for _ in range(10):-        time.sleep(0.7)++    def _tree_candidates():+        """The LIB_TREE dataview lives near the searchCtrl: collect tall sibling+        windows under the Libraries pane (wx generic controls share bland class+        names, so geometry is the discriminator)."""+        cands = []+        try:+            pane = user32.GetParent(user32.GetParent(edit)) or user32.GetParent(edit)+            WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.wintypes.HWND,+                                             ctypes.wintypes.LPARAM)+            def cb(hwnd, _l):+                r = ctypes.wintypes.RECT()+                user32.GetWindowRect(hwnd, ctypes.byref(r))+                if (r.bottom - r.top) > 200 and hwnd != edit:+                    cands.append((hwnd, r))+                return True+            cbp = WNDENUMPROC(cb)+            _callbacks.append(cbp)+            user32.EnumChildWindows(pane, cbp, 0)+        except Exception:+            pass+        return cands++    def _title_ok():         t = (_get_symbol_editor_title() or "").lower()-        if verify in t and "no symbol loaded" not in t:+        return verify in t and "no symbol loaded" not in t++    # Round 1: Enter posted straight to each tree candidate (the payload's own+    # in-process technique, aimed from outside).+    for hwnd, _r in _tree_candidates():+        user32.PostMessageW(hwnd, WM_KEYDOWN, VK_RETURN, 0)+        user32.PostMessageW(hwnd, WM_KEYUP, VK_RETURN, 0)+    for _ in range(6):+        time.sleep(0.7)+        if _title_ok():+            return True++    # Round 2: double-click the selected row (first visible row sits just under+    # the header) — pure PostMessage, still zero focus.+    WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, MK_LBUTTON = 0x0201, 0x0202, 0x0203, 0x0001+    for hwnd, r in _tree_candidates():+        w = r.right - r.left+        x, y = max(10, w // 3), 58  # inside the item column, ~second row band+        lp = (y << 16) | (x & 0xFFFF)+        user32.PostMessageW(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, lp)+        user32.PostMessageW(hwnd, WM_LBUTTONUP, 0, lp)+        user32.PostMessageW(hwnd, WM_LBUTTONDBLCLK, MK_LBUTTON, lp)+        user32.PostMessageW(hwnd, WM_LBUTTONUP, 0, lp)+    for _ in range(8):+        time.sleep(0.7)+        if _title_ok():             return True     return False