# 功能提示系統分析報告

## 2026-05-01 討論整理

---

## 一、系統類型分類

```
類型             程式                                           功能提示機制                     row 0
──────────────   ────────────────────────────────────────────   ────────────────────────────   ──────────────────────
單據系統          BUYSELL, QUOT, INQUIRY, DOMCMP,               BottomMessage                   TopMessage
                 DOMPUR, DOMARR, FORCMP, FORPUR,
                 FORARR, ENQUIRY, DOMORD, DOMSCH, STOCK
視窗系統          WINDOW, DBFWIN                                WindowMessage                   繼承上層 TopMessage
瀏覽表單系統      INFORM, P113                                   WinFormMessage → BottomMessage  TopMessage（P11x 呼叫）
陣列瀏覽系統      ARRAY, BRRAY                                   ARRAY:WindowMessage / BRRAY:無  繼承上層 TopMessage
主選單            MENU                                          無（MenuBar 佔 row 0）            MenuBar（不可有 hotzone）
```

---

## 二、aMsReg 作用域（已驗證）

### 2.1 PRIVATE aMsReg 宣告位置

```
程式             行號        用途
──────────────   ─────────   ──────────────────────
STK.PRG          35          PUBLIC aMsReg（全域初始值）
MENU.PRG         27          PullDownMenu 選單層
MENU.PRG         71, 100     選單層級切換時重設
WINDOW.PRG       40          視窗系統隔離
DBFWIN.PRG       41          視窗系統隔離
WINFORM.PRG      24          瀏覽表單系統隔離
ARRAY.PRG        38          陣列瀏覽隔離
TABLE.PRG        42          表格瀏覽隔離
DIALOG.PRG       36          對話框隔離
TLIB.PRG         53          BottomMenu 隔離
TLIB.PRG         579         MessageBox 隔離
TLIB.PRG         1083        QuitSystem 清除
QUOT.PRG         5944        AskPartsBox 子函式隔離
```

### 2.2 單據系統的 aMsReg 繼承鏈（已驗證）

```
PullDownMenu() [MENU.PRG:27 PRIVATE aMsReg := {}]
  └─ &cProgString("StartUp") [MENU.PRG:191 在迴圈內部呼叫]
       └─ P211("StartUp") [P21.PRG:16 無 PRIVATE aMsReg]
            └─ BuySell() [BUYSELL.PRG:25 無 PRIVATE aMsReg]
                 → 繼承 PullDownMenu 的 PRIVATE aMsReg
```

13 個單據系統全部不宣告 PRIVATE aMsReg，全部繼承 MENU 的。

### 2.3 WINFORM 的 scope 問題

```
PullDownMenu() [PRIVATE aMsReg := {}]  ← scope A
  └─ P111("StartUp")
       ├─ TopMessage("《廠商基本資料》") → 註冊到 scope A
       └─ WinForm() [PRIVATE aMsReg := {}]  ← scope B（遮蔽 A）
            └─ 主迴圈 HoverUpdate/HitTest 查 scope B（空的）
               → 找不到 TopMessage 的熱區
```

---

## 三、功能提示的需求特性

### 3.1 累加與遮蔽

```
場景                             行為                     機制
──────────────────────────────   ───────────────────────  ──────────────────
進入單據                         建立功能提示               BottomMessage 註冊
切換操作狀態（表頭/表身）         更新功能提示               重新呼叫 BottomMessage
進入子視窗（F1/F2/F3/F4）        遮蔽，顯示子視窗的提示      PRIVATE aMsReg 遮蔽
離開子視窗                       自動恢復上層提示           PRIVATE 離開 scope
功能出現順序不固定                SetKey 觸發，隨機組合      stack 行為處理
```

### 3.2 TopMessage hotzone 的需求

- **出現條件**：只要 TopMessage 顯示就有熱區，不顯示就沒有
- **天敵**：主選單（MenuBar 佔 row 0，不可有 TopMessage hotzone）
- **穿透性**：不管進幾層子功能，只要 TopMessage 還在 row 0，hotzone 就要有效
- **資料來源**：讀取當前 scope 的功能提示（BottomMessage/WindowMessage 收集的）

---

## 四、功能提示的三個函式職責

```
函式                              職責                                  已實作
────────────────────────────────  ───────────────────────────────────  ────────────────────
TopMessage                        顯示 row 0 + 註冊觸發熱區              部分（scope 問題未解）
BottomMessage / WindowMessage     顯示功能提示 + 收集資料 + 註冊熱區      已完善
ShowHotKeys                       讀取資料 + PickList 呈現 + 送出按鍵    部分（資料來源待確認）
```

---

## 五、功能提示資料的來源

### 5.1 集中管理的（經過 BottomMessage / WindowMessage）

```
來源函式           資料存放               使用者
────────────────   ────────────────────   ─────────────────────────────
BottomMessage      M->aMouse（回傳值）    BUYSELL, WINFORM, 單據系統
WindowMessage      M->aMouse（回傳值）    WINDOW, DBFWIN, ARRAY
```

### 5.2 散落的（直接 MsAddKey，不經過上述函式）

```
位置                    說明                              aMouse 有無
─────────────────────   ─────────────────────────────────  ──────────
DIALOG.PRG:74-75        Dialog 的 PgDn / ESC              無
MENU.PRG:81, 118        MenuBar 選項                      無（不需要）
TLIB.PRG:56             BottomMenu 功能鍵                 無
TLIB.PRG:308            TopMessage hint mode PgDn         無
TLIB.PRG:319            TopMessage 標題熱區（新加）        無
TLIB.PRG:323            TopMessage 公司名稱 K_SH_F10      無
TOOLS.PRG:2508-2519     查詢畫面 PgDn / ALT+H             無
WINFORM.PRG:679, 775    WinForm GET 欄位點擊              無
```

---

## 六、aMouse vs aMsReg — 為什麼有兩個陣列

### 6.1 設計目的

```
                 aMouse                                    aMsReg
─────────────    ─────────────────────────────────────     ─────────────────────────────────────────────
誰建立           BottomMessage/WindowMessage 的回傳值       MsAddKey 直接註冊
存放位置         PRIVATE 變數（呼叫者存）                    PRIVATE 變數
格式             {row, col, width,                         {row, colS, colE,
                  displayText, color, nKey}                 nKey, flag, procName, sayStr, color, realFunc}
比對方式         nMCol >= col .AND. nMCol < col+width       nMCol >= colS .AND. nMCol <= colE
Hover 處理       HoverCheck(aMouse, nHover) 傳入參數        HoverUpdate() 直接讀 aMsReg
Click 處理       MsClick(aMouse, nHover) 回傳 nKey          HitTest() 回傳整個 aItem
誰的資料         功能提示（按鍵說明）                         所有滑鼠熱區（任何位置）
用途             專門處理「功能提示列」hover+click           通用滑鼠熱區（任何位置）
```

### 6.2 為什麼要分兩個

- **aMouse** 是「功能提示專用」的輕量格式，由 BottomMessage/WindowMessage 產生
- **aMsReg** 是「通用滑鼠熱區」，任何地方都可以用 MsAddKey 註冊

### 6.3 在 BrowseKey 中的處理順序

```
K_MOUSEMOVE:
  1. row 0 公司名稱硬編碼 hover（SaveScreen 方式）
  2. HoverCheck(aMouse)     ← 功能提示列的 hover
  3. HoverUpdate()          ← aMsReg 通用熱區的 hover

K_LBUTTONDOWN:
  1. row 0, col>=56 硬編碼  ← 公司名稱 → K_SH_F10
  2. browse 內部             ← return nKey（TBMouse）
  3. MsClick(aMouse)        ← 功能提示列的 click
  4. HitTest()              ← aMsReg 通用熱區的 click
```

### 6.4 ShowHotKeys 的資料關係

- ShowHotKeys 要**讀取**的資料 → **aMouse**（功能提示的內容）
- ShowHotKeys 的**觸發點** → **aMsReg**（TopMessage 標題熱區）

---

## 七、公司名稱 K_SH_F10 的運作方式（參考）

### 在 BUYSELL 等單據系統中

```
TopMessage 註冊 MsAddKey → aMsReg
  → GETSYS K_LBUTTONDOWN → MsHitTest → hb_keyPut(K_SH_F10)
  → SetKey(K_SH_F10) handler 觸發
```

### 在 WINFORM 中

```
TopMessage 註冊到外層 aMsReg（scope A）
WinForm 自己的 aMsReg（scope B）是空的
但公司名稱仍然有效 — 因為 BrowseKey 有硬編碼：
  K_MOUSEMOVE:    row 0 右側 SaveScreen/RestScreen hover（行 1048-1060）
  K_LBUTTONDOWN:  row 0 col>=56 → hb_keyPut(K_SH_F10)（行 1092-1094）
不依賴 aMsReg，所以不受 scope 影響
```

---

## 八、HitTest 中新增的 nKey=NIL + realFunction 機制

### 設計

- aMsReg 第 9 元素存 codeblock 或函式名
- HitTest 檢測到 nKey=NIL 時，直接 Eval/Do，不走 hb_keyPut
- 回傳 NIL（caller 不會嘗試 hb_keyPut(NIL)）

### 已修改位置

- mouse_handler.prg AddKey: 第 7 參數 cRealFunc
- mouse_handler.prg HitTest: nKey=NIL 處理邏輯
- mouse_handler.prg MsAddKey wrapper: 第 7 參數
- ReadKey/BrowseKey: 已還原為原始 hb_keyPut（由 HitTest 統一處理）

### 現有問題

- 依賴 aMsReg scope — WINFORM 中 TopMessage 的熱區在錯誤的 scope
- hover 也依賴 aMsReg — 找不到就不會變色

---

## 九、待解決問題

1. **TopMessage hotzone 的 scope 問題**
   - 需要穿透所有 PRIVATE stack 層級
   - 但不能在 MenuBar 出現
   - 需要跟著 TopMessage 的顯示/隱藏走

2. **ShowHotKeys 的資料來源**
   - 主要來自 M->aMouse（BottomMessage/WindowMessage 收集）
   - 散落的 MsAddKey 不在 aMouse 中，如何處理？

3. **觸發機制的全域性**
   - 目前用 aMsReg 的 MsAddKey → 受 scope 限制
   - 需要一個不受 scope 影響的觸發機制

---

## 十、下一步討論方向

- TopMessage hotzone 的持久化方案（oMs DATA? 硬編碼? 其他?）
- ShowHotKeys 資料來源的統一方案
- 散落的功能提示如何整合
- 累加/遮蔽與 TopMessage hotzone 的協調
