Skip to content
Merged
24 changes: 24 additions & 0 deletions WHATS_NEW.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# What's New — AutoControl

## What's new (2026-06-25) — Container Selection + View Switching (Selection / MultipleView)

Read what's selected in a listbox/grid, and switch Explorer-style views. Full reference: [`docs/source/Eng/doc/new_features/v201_features_doc.rst`](docs/source/Eng/doc/new_features/v201_features_doc.rst).

- **`get_selection` / `list_views` / `set_view`** (`AC_get_selection`, `AC_list_views`, `AC_set_view`): `select_control_item` selects *one* item, but the container-level `SelectionPattern` answers "what is currently selected, and may it select multiple?" — the assertion target after selecting. `MultipleViewPattern` switches a control between its views (Explorer's list / details / tile / thumbnail), a precondition that otherwise needs fragile menu clicking. `get_selection` returns `{items, can_select_multiple, is_required}`, `list_views` returns `{current, views}`, and `set_view` switches by view name. Dispatched through the injectable accessibility backend seam (headless-testable via a fake backend; real UIA in the Windows backend). No `PySide6`.

## What's new (2026-06-25) — Advanced TextPattern (find / select / read attributes)

Search a control's text, select a match to replace it, and read font/colour formatting. Full reference: [`docs/source/Eng/doc/new_features/v200_features_doc.rst`](docs/source/Eng/doc/new_features/v200_features_doc.rst).

- **`find_control_text` / `select_control_text` / `control_text_attributes`** (`AC_find_control_text`, `AC_select_control_text`, `AC_control_text_attributes`): `ax_text` shipped the three whole-range *reads*, but couldn't search for a substring, select a found range, or read text formatting — needed to assert "the error word is red and bold" or to place the selection at matched text before typing. This rounds out TextPattern: `find_control_text` searches the real content (not OCR) via `FindText`, `select_control_text` finds + selects a range so the next keystrokes replace it, and `control_text_attributes` reads `{font_name, font_size, bold, italic, foreground_color}`. Dispatched through the injectable accessibility backend seam (headless-testable via a fake backend; real UIA in the Windows backend). No `PySide6`.

## What's new (2026-06-25) — MSAA Bridge for Legacy Controls (LegacyIAccessible)

Automate the long tail of old Win32 controls that expose nothing via modern UIA. Full reference: [`docs/source/Eng/doc/new_features/v199_features_doc.rst`](docs/source/Eng/doc/new_features/v199_features_doc.rst).

- **`legacy_info` / `legacy_default_action`** (`AC_legacy_info`, `AC_legacy_default_action`): many legacy Win32 / MFC / Delphi controls expose nothing useful via modern UIA patterns (`control_get_value` / `control_invoke` / `control_toggle` all return None), yet they're fully described through the MSAA `IAccessible` bridge — Name, Value, Description, Role, State and a **DefaultAction**. This reads that info and fires the default action via `LegacyIAccessiblePattern` — the last-resort fallback that makes old apps automatable. Dispatched through the injectable accessibility backend seam (headless-testable via a fake backend; real UIA in the Windows backend). No `PySide6`.

## What's new (2026-06-25) — Move / Resize Elements + Window State (UIA Transform + Window)

Move a floating panel, resize a control, and know if a window is modal-blocked. Full reference: [`docs/source/Eng/doc/new_features/v198_features_doc.rst`](docs/source/Eng/doc/new_features/v198_features_doc.rst).

- **`move_element` / `resize_element` / `set_window_state` / `window_interaction_state`** (`AC_move_element`, `AC_resize_element`, `AC_set_window_state`, `AC_window_interaction_state`): this is UIA-**element-level**, not the HWND/title-level geometry in `window_layout`. `TransformPattern` moves/resizes a specific control or floating panel (dockable toolbars, MDI children, splitters) with no top-level window of its own; `WindowPattern` minimizes/maximizes a window and reports its **interaction state** (`ready` / `blocked_by_modal` / `not_responding`) — a reliable "is this window ready or modal-blocked?" signal pixel/title polling can't give. Dispatched through the injectable accessibility backend seam (headless-testable via a fake backend; real UIA in the Windows backend). No `PySide6`.

## What's new (2026-06-25) — Table Headers + Cell Addressing (UIA TablePattern)

Assert "the Status column of row 5 says Shipped" — by header, not by guessing indices. Full reference: [`docs/source/Eng/doc/new_features/v197_features_doc.rst`](docs/source/Eng/doc/new_features/v197_features_doc.rst).
Expand Down
46 changes: 46 additions & 0 deletions docs/source/Eng/doc/new_features/v198_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
Move / Resize Elements + Window State (UIA Transform + Window)
==============================================================

This is **UIA-element-level**, not the HWND / title-level geometry in
``window_layout`` / ``window_geometry``. ``TransformPattern`` moves and resizes a
specific control or floating panel (dockable toolbars, MDI children, splitters)
that has no top-level window of its own; ``WindowPattern`` minimizes / maximizes a
window and — most usefully — reports its **interaction state**, a reliable "is
this window ready or modal-blocked?" signal that pixel or title polling can't give.

* :func:`move_element` / :func:`resize_element` — TransformPattern,
* :func:`set_window_state` — minimize / maximize / restore,
* :func:`window_interaction_state` — the readiness / modal-blocked signal.

Each is a thin dispatch onto the injectable ``accessibility.backends.get_backend()``
seam — headless-testable via a fake backend; the real UIA calls live in the
Windows backend. Imports no ``PySide6``.

Headless API
------------

.. code-block:: python

from je_auto_control import (move_element, resize_element,
set_window_state, window_interaction_state)

move_element(100, 200, name="Tool Palette") # reposition a floating panel
resize_element(640, 480, name="Preview") # resize a control
set_window_state("maximized", name="Editor") # normal / maximized / minimized

if window_interaction_state(name="Editor") == "ready":
... # not "blocked_by_modal" / "not_responding" — safe to drive

The element / window is located by ``name`` / ``role`` / ``app_name`` /
``automation_id`` (same as the other native-control actions). The actions return
``bool``; ``window_interaction_state`` returns ``ready`` / ``blocked_by_modal`` /
``not_responding`` / ``running`` / ``closing`` (or ``None`` if not found).

Executor commands
-----------------

``AC_move_element`` (``x`` / ``y``), ``AC_resize_element`` (``width`` /
``height``), ``AC_set_window_state`` (``state``) and
``AC_window_interaction_state`` (``{state}``). They are exposed as the matching
``ac_*`` MCP tools (the actions destructive, the read read-only) and as Script
Builder commands under **Native UI**.
43 changes: 43 additions & 0 deletions docs/source/Eng/doc/new_features/v199_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
MSAA Bridge for Legacy Controls (LegacyIAccessible)
===================================================

Many legacy Win32 / MFC / Delphi controls expose **nothing useful** via the modern
UI Automation patterns — ``control_get_value`` / ``control_invoke`` /
``control_toggle`` all return None or do nothing — yet they are fully described
through the MSAA ``IAccessible`` bridge: a Name, Value, Description, Role, State
and a **DefaultAction**. ``legacy_accessible`` is the last-resort fallback that
still reads that info and fires the default action, making the long tail of old
apps automatable.

* :func:`legacy_info` — the MSAA fields ``{name, value, description,
default_action, role, state}``,
* :func:`legacy_default_action` — fire the control's default action.

Each is a thin dispatch onto the injectable ``accessibility.backends.get_backend()``
seam — headless-testable via a fake backend; the real ``LegacyIAccessiblePattern``
calls live in the Windows backend. Imports no ``PySide6``.

Headless API
------------

.. code-block:: python

from je_auto_control import (legacy_info, legacy_default_action,
control_invoke)

# Modern patterns came up empty? Fall back to MSAA:
if not control_invoke(name="Apply"):
info = legacy_info(name="Apply") # {"default_action": "Press", ...}
legacy_default_action(name="Apply") # fires the MSAA default action

The control is located by ``name`` / ``role`` / ``app_name`` / ``automation_id``
(same as the other native-control actions). ``legacy_info`` returns the MSAA info
dict (``role`` / ``state`` are the raw MSAA numbers) or ``None`` if the control or
pattern isn't found; ``legacy_default_action`` returns ``bool``.

Executor commands
-----------------

``AC_legacy_info`` (``{found, info}``) and ``AC_legacy_default_action``. They are
exposed as the matching ``ac_*`` MCP tools (info read-only, the action
destructive) and as Script Builder commands under **Native UI**.
48 changes: 48 additions & 0 deletions docs/source/Eng/doc/new_features/v200_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
Advanced TextPattern — Find / Select / Read Attributes
======================================================

``ax_text`` shipped the three whole-range *reads* (document / selection / visible
text). It could not **search** for a substring, **select** a found range, or read
text **formatting attributes** — needed to assert "the error word is red and
bold" or to place the caret / selection at matched text before typing. This rounds
TextPattern out from "dump the text" to "interrogate and manipulate" it.

* :func:`find_control_text` — whether ``text`` occurs in the control
(TextPattern.FindText, searches the real content, not OCR),
* :func:`select_control_text` — find ``text`` and select its range, so the next
keystrokes replace it (FindText + Select),
* :func:`control_text_attributes` — the selection's font / colour formatting.

Each is a thin dispatch onto the injectable ``accessibility.backends.get_backend()``
seam — headless-testable via a fake backend; the real UIA calls live in the
Windows backend. Imports no ``PySide6``.

Headless API
------------

.. code-block:: python

from je_auto_control import (find_control_text, select_control_text,
control_text_attributes, type_text)

if find_control_text("TODO", name="Editor"):
select_control_text("TODO", name="Editor") # selection now spans "TODO"
type_text("DONE") # replaces it

control_text_attributes(name="Editor")
# {"font_name": "Consolas", "font_size": 11.0, "bold": True,
# "italic": False, "foreground_color": 16711680}

``ignore_case`` (default ``True``) controls the search. The control is located by
``name`` / ``role`` / ``app_name`` / ``automation_id`` (same as the other
TextPattern reads). ``find_control_text`` / ``select_control_text`` return
``bool``; ``control_text_attributes`` returns the formatting dict (values may be
``None`` where the range spans mixed formatting) or ``None`` if not found.

Executor commands
-----------------

``AC_find_control_text`` / ``AC_select_control_text`` (``text`` / ``ignore_case``)
and ``AC_control_text_attributes`` (``{found, attributes}``). They are exposed as
the matching ``ac_*`` MCP tools (find / attributes read-only, select destructive)
and as Script Builder commands under **Native UI**.
45 changes: 45 additions & 0 deletions docs/source/Eng/doc/new_features/v201_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
Container Selection + View Switching (Selection / MultipleView)
===============================================================

``select_control_item`` (SelectionItemPattern) selects *one* item; the
container-level ``SelectionPattern`` answers the natural follow-up — **what is
currently selected** in a listbox / grid / tab, and **may it select multiple?** —
the assertion target after selecting. ``MultipleViewPattern`` switches a control
between its views (Explorer's list / details / tile / thumbnail), a common
precondition that otherwise needs fragile menu clicking.

* :func:`get_selection` — ``{items, can_select_multiple, is_required}``,
* :func:`list_views` — ``{current, views: [...]}``,
* :func:`set_view` — switch to a named view.

Each is a thin dispatch onto the injectable ``accessibility.backends.get_backend()``
seam — headless-testable via a fake backend; the real UIA calls live in the
Windows backend. Imports no ``PySide6``.

Headless API
------------

.. code-block:: python

from je_auto_control import get_selection, list_views, set_view

get_selection(name="File List")
# {"items": ["report.pdf", "notes.txt"], "can_select_multiple": True,
# "is_required": False}

list_views(name="File List")
# {"current": "Details", "views": ["List", "Details", "Tiles"]}
set_view("Tiles", name="File List") # switch the view

The control is located by ``name`` / ``role`` / ``app_name`` / ``automation_id``
(same as the other native-control actions). ``get_selection`` / ``list_views``
return their dict (or ``None`` if the control or pattern isn't found);
``set_view`` returns ``bool`` (False when the named view isn't supported).

Executor commands
-----------------

``AC_get_selection`` (``{found, selection}``), ``AC_list_views`` (``{found,
views}``) and ``AC_set_view`` (``view``). They are exposed as the matching
``ac_*`` MCP tools (the reads read-only, ``set_view`` destructive) and as Script
Builder commands under **Native UI**.
41 changes: 41 additions & 0 deletions docs/source/Zh/doc/new_features/v198_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
移動 / 縮放元素 + 視窗狀態(UIA Transform + Window)
==================================================

這是 **UIA 元素層級**,而非 ``window_layout`` / ``window_geometry`` 的 HWND / title 層級幾何。
``TransformPattern`` 移動與縮放某個沒有自己頂層視窗的特定控制項或浮動面板(可停靠工具列、MDI
子視窗、分隔器);``WindowPattern`` 最小化 / 最大化視窗,並——最有用地——回報其 **interaction
state(互動狀態)**,這是像素或標題輪詢給不出的可靠「這視窗是否就緒 / 被 modal 擋住?」訊號。

* :func:`move_element` / :func:`resize_element` ——TransformPattern,
* :func:`set_window_state` ——最小化 / 最大化 / 還原,
* :func:`window_interaction_state` ——就緒 / 被 modal 擋住的訊號。

每個都是對可注入的 ``accessibility.backends.get_backend()`` 接縫的薄分派——可透過注入 fake
backend 進行無頭測試;真正的 UIA 呼叫位於 Windows 後端。不匯入 ``PySide6``。

無頭 API
--------

.. code-block:: python

from je_auto_control import (move_element, resize_element,
set_window_state, window_interaction_state)

move_element(100, 200, name="Tool Palette") # 重新定位浮動面板
resize_element(640, 480, name="Preview") # 縮放控制項
set_window_state("maximized", name="Editor") # normal / maximized / minimized

if window_interaction_state(name="Editor") == "ready":
... # 不是 "blocked_by_modal" / "not_responding"——可安全驅動

元素 / 視窗以 ``name`` / ``role`` / ``app_name`` / ``automation_id`` 定位(與其他原生控制動作
相同)。動作回傳 ``bool``;``window_interaction_state`` 回傳 ``ready`` / ``blocked_by_modal`` /
``not_responding`` / ``running`` / ``closing``(找不到則為 ``None``)。

執行器指令
----------

``AC_move_element``(``x`` / ``y``)、``AC_resize_element``(``width`` / ``height``)、
``AC_set_window_state``(``state``)與 ``AC_window_interaction_state``(``{state}``)。皆以對應的
``ac_*`` MCP 工具(動作類為破壞性、讀取類為唯讀)及 Script Builder 指令(位於 **Native UI**
分類下)形式提供。
38 changes: 38 additions & 0 deletions docs/source/Zh/doc/new_features/v199_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
舊式控制項的 MSAA 橋接(LegacyIAccessible)
==========================================

許多舊式 Win32 / MFC / Delphi 控制項透過現代 UI Automation 模式**完全不提供有用資訊**——
``control_get_value`` / ``control_invoke`` / ``control_toggle`` 都回 None 或毫無作用——但它們透過
MSAA ``IAccessible`` 橋接卻有完整描述:Name、Value、Description、Role、State,以及一個
**DefaultAction**。``legacy_accessible`` 就是那個最後手段的後備:仍能讀取這些資訊並觸發預設動作,
讓大量舊應用程式得以自動化。

* :func:`legacy_info` ——MSAA 欄位 ``{name, value, description, default_action, role, state}``,
* :func:`legacy_default_action` ——觸發控制項的預設動作。

每個都是對可注入的 ``accessibility.backends.get_backend()`` 接縫的薄分派——可透過注入 fake
backend 進行無頭測試;真正的 ``LegacyIAccessiblePattern`` 呼叫位於 Windows 後端。不匯入
``PySide6``。

無頭 API
--------

.. code-block:: python

from je_auto_control import (legacy_info, legacy_default_action,
control_invoke)

# 現代模式撲空?退回 MSAA:
if not control_invoke(name="Apply"):
info = legacy_info(name="Apply") # {"default_action": "Press", ...}
legacy_default_action(name="Apply") # 觸發 MSAA 預設動作

控制項以 ``name`` / ``role`` / ``app_name`` / ``automation_id`` 定位(與其他原生控制動作相同)。
``legacy_info`` 回傳 MSAA 資訊字典(``role`` / ``state`` 為原始 MSAA 數字),找不到控制項或模式
則回傳 ``None``;``legacy_default_action`` 回傳 ``bool``。

執行器指令
----------

``AC_legacy_info``(``{found, info}``)與 ``AC_legacy_default_action``。皆以對應的 ``ac_*`` MCP
工具(info 為唯讀、動作為破壞性)及 Script Builder 指令(位於 **Native UI** 分類下)形式提供。
44 changes: 44 additions & 0 deletions docs/source/Zh/doc/new_features/v200_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
進階 TextPattern——搜尋 / 選取 / 讀取屬性
========================================

``ax_text`` 先前提供三種整段*讀取*(document / selection / visible 文字)。它無法**搜尋**子字串、
**選取**找到的範圍,或讀取文字的**格式屬性**——而這些正是斷言「錯誤字是紅色且粗體」或在輸入前
把游標 / 選取定位到匹配文字所需。本次把 TextPattern 從「傾印文字」擴充為「查詢與操作」文字。

* :func:`find_control_text` ——``text`` 是否出現在控制項中(TextPattern.FindText,搜尋真正的
內容,而非 OCR),
* :func:`select_control_text` ——找到 ``text`` 並選取其範圍,讓接下來的按鍵取代它
(FindText + Select),
* :func:`control_text_attributes` ——選取範圍的字型 / 顏色格式。

每個都是對可注入的 ``accessibility.backends.get_backend()`` 接縫的薄分派——可透過注入 fake
backend 進行無頭測試;真正的 UIA 呼叫位於 Windows 後端。不匯入 ``PySide6``。

無頭 API
--------

.. code-block:: python

from je_auto_control import (find_control_text, select_control_text,
control_text_attributes, type_text)

if find_control_text("TODO", name="Editor"):
select_control_text("TODO", name="Editor") # 選取範圍現在涵蓋 "TODO"
type_text("DONE") # 取代它

control_text_attributes(name="Editor")
# {"font_name": "Consolas", "font_size": 11.0, "bold": True,
# "italic": False, "foreground_color": 16711680}

``ignore_case``(預設 ``True``)控制搜尋。控制項以 ``name`` / ``role`` / ``app_name`` /
``automation_id`` 定位(與其他 TextPattern 讀取相同)。``find_control_text`` /
``select_control_text`` 回傳 ``bool``;``control_text_attributes`` 回傳格式字典(範圍跨越混合
格式時某些值可能為 ``None``),找不到則回傳 ``None``。

執行器指令
----------

``AC_find_control_text`` / ``AC_select_control_text``(``text`` / ``ignore_case``)與
``AC_control_text_attributes``(``{found, attributes}``)。皆以對應的 ``ac_*`` MCP 工具
(find / attributes 為唯讀、select 為破壞性)及 Script Builder 指令(位於 **Native UI** 分類下)
形式提供。
Loading
Loading