# WinForm SUBFORM2（明細 GET 欄位）滑鼠版面編輯 - 架構研究與實施設計

日期：2026-07-01　狀態：待討論（設計稿）
相關檔案：INITIAL.PRG（InitialForm / ChangeFormSetting）、WINFORM.PRG（主迴圈）、P113.PRG（PartsForm）、mouse_handler.prg、picklist.prg、tools.prg（WindowMessage 可點提示列）

---

## 一、現況架構（研究結果）

### 1-1. 資料模型：SUBFORM2.DBF（每筆 = 一個 SAY 標籤 + GET 輸入格）
key = CALIAS(10)+CPRIORITY(2)+CFORMAT(2)+CORDER(2)，一個表單底下多筆，用 CORDER 排序。

| 用途 | 欄位 | 型別 |
|------|------|------|
| 標籤位置 | NSAYROW / NSAYCOL | N |
| GET 輸入格位置 | NGETROW / NGETCOL | N |
| SIZE 寬度 | NGETWIDTH | N(3) |
| PICTURE | CPICTURE | C(20) |
| WHEN | CWHENFUN | C(60) |
| VALID | CVALIDFUN | C(79) |
| 標籤文字 | CSAYSTRING | C(30) |
| 欄位變數 | CGETVAR | C(40) |
| GET 取值/按鍵 block | CGETBLOCK / CINKEYBLK | C |
| 啟用/停用 GET | NSUBTYPE (ENABLE/DISABLE_GET_TYPE) | N |
| BOX 模式左上 | NTOP / NLEFT | N |

### 1-2. 座標是「相對」的（重要！）
InitialForm 畫欄位時（INITIAL.PRG 531-604），座標 = 視窗有效基準 + 相對偏移：

- 垂直版面(bit0=0)：
  - SAY：row = nEffTop + NSAYROW、col = nEffCenter + NSAYCOL
  - GET：row = nEffTop + NGETROW、col = nEffCenter + NGETCOL（為 0 時用 Row()/Col()+1 接續）
- 水平版面(bit0=1)：
  - SAY：row = nEffCenter + NSAYROW、col = nEffLeft + NSAYCOL
  - GET：row = nEffCenter + NGETROW、col = nEffLeft + NGETCOL

因為是相對座標，視窗移動 / splitter 調比例後 GET 自動跟著（本次 splitter 就靠這個）。滑鼠搬移欄位時，寫回的也是相對偏移（點擊絕對座標 減 基準）。

### 1-3. GET 物件的 cargo（InitialForm 568-575 / 609-615）
GetList[n]:cargo = { lInputWay, cAlias, ENABLE/DISABLE, readVar, bGetBlock, oBrowse, bInkeyBlock }
目前沒有存 SUBFORM2 的 RecNo。滑鼠點擊要回寫哪一筆，需在此 cargo 增存 RecNo（實施步驟 S1）。
GET 物件本身有 :row / :col（螢幕絕對位置）、寬度可由 PICTURE / NGETWIDTH 得知，命中判斷可用之。

### 1-4. 既有 Alt+F（ChangeFormSetting，INITIAL.PRG:927）已能編輯全部屬性
進 Alt+F 選欄位後 sub-menu：
```
1.MoveSubBox   改位置（鍵盤 DragBox，寫 NSAYROW/NSAYCOL 或 NTOP/NLEFT）
5.EditGetWidth SIZE (NGETWIDTH)   6.EditPicture PICTURE
7.EditWhenFun  WHEN               8.EditValidFun VALID
3.EditSayString 4.EditGetString 9.EditInKeyBlk A.EditGetEnable B.Delete
```
每個動作模式相同：RecordLock 到 REPLACE SubForm2 欄位 到 RecordUnLock 到 RESTORE SCREEN 到 GetList:={} 到 InitialForm 重畫。存 DBF 持久化 + 即時刷新。這是已驗證安全機制，滑鼠版必須沿用，不可另寫一套。

---

## 二、目標與設計原則

### 2-1. 目標
把版面調整從純鍵盤（Alt+F 逐層選單 + 方向鍵 DragBox）升級成滑鼠直覺操作：搬移欄位、改寬、改 PICTURE/WHEN/VALID，全部沿用「寫 SubForm2 + InitialForm 重畫」機制、持久化。

### 2-2. 最重要：編輯模式 vs 輸入模式（安全關鍵）
表單平常是輸入資料用（READ）：滑鼠點欄位 = 游標移該欄輸入。若「點欄位=搬移」會跟輸入衝突。
必須有明確「版面編輯模式」開關：
- 進入編輯模式（Alt+F 或新熱鍵）才啟用滑鼠拖曳/改寬/雙擊。
- 編輯模式中：頂列/底列提示、游標在欄位上變形、選取欄位反白。
- ESC 離開編輯模式回輸入。非編輯模式滑鼠完全不介入。

### 2-3. 操作方式限制（使用者現況）
右鍵已全域綁 ESC（取消/返回）→ 本設計「不使用右鍵」做功能選單，改用下列更順手的方式。

---

## 三、介面設計（滑鼠操作，避開右鍵）

### 3-1. 進入/離開
- Alt+F（或新熱鍵，如 Ctrl+F11）進入「版面編輯模式」。原鍵盤選單保留為備援。
- 提示列（底部，可點）：[選取:單擊] [搬移:拖曳] [寬度:拖右緣] [屬性:雙擊] [ESC:離開]

### 3-2. 選取欄位（單擊）
- 單擊某 GET 欄位 到 該欄反白外框 + 底部顯示欄位資訊（變數/寬度/PICTURE）。作為後續動作對象。

### 3-3. 搬移欄位（拖曳，主功能）
1. 游標移到欄位 到 游標變四向、外框高亮。
2. 點住拖曳 到 即時預覽外框跟滑鼠。
3. 放開 到 反算新 NSAYROW/NSAYCOL（＋NGETROW/NGETCOL 同步位移）寫回 SubForm2 到 InitialForm 重畫。
4. 邊界夾值：不可超出表單有效區。

### 3-4. 改寬度（SIZE，拖右緣）
- GET 輸入格右緣 到 游標變左右箭頭 到 拖曳改 NGETWIDTH 到 重畫。

### 3-5. 屬性編輯（雙擊 + 底部可點功能列，取代右鍵）
兩種擇一或並用：
- (a) 雙擊欄位 到 PickList 彈出屬性選單（PopAtMouse）：改寬度 / PICTURE / WHEN / VALID / 標籤文字 / 啟用切換 / 刪除。
- (b) 底部可點功能列（沿用 WindowMessage + MsAddKey 熱區，即先前對話框可點提示列做法）：先單擊選取欄位，再點底部按鈕 [寬度][PICTURE][WHEN][VALID][標籤][啟用][刪除]。
每項呼叫既有 SingleQuery / MessageBox（重用 ChangeFormSetting 內同邏輯）到 寫回 到 重畫。
建議 (b) 為主（純滑鼠、不需記雙擊、可視化清楚），(a) 為輔。

### 3-6. 游標回饋
沿用本次 splitter 的 PLSetMouseCursor：搬移=四向、改寬=左右(3)。命中判斷用 GET :row/:col + 寬度、SAY row/col + 標籤長度。

---

## 四、實施步驟（分階段，低風險先行）

### S1. InitialForm 存 SUBFORM2 RecNo 進 GET cargo（前置，零風險）
- 568-575 與 609-615 兩處 cargo 末尾 AAdd( cargo, SubForm2->(RecNo()) )。
- 新增 cargo 常數（WinForm.ch）。只增資料不改行為，80x25 零回歸。

### S2. 欄位命中 helper（唯讀，零風險）
- FormGetHitTest( GetList, nRow, nCol ) 回傳點到第幾個 GET（比對 :row/:col + 寬度、含 SAY 範圍）。
- FormGetRect( oGet ) 算欄位外框（含標籤）。

### S3. 版面編輯模式外殼（介面骨架）
- 新函式 FormLayoutEdit( cAlias, cFormat, nCtrlFlag, @GetList, oBrowse )：
  - 進入畫提示列（可點）、SaveScreen。
  - 迴圈 Inkey：MOUSEMOVE 到 游標/hover 外框；單擊 到 選取；雙擊 到 屬性選單；LBUTTON 拖曳 到 搬移；ESC 到 離開。
- 由 Alt+F 呼叫（與原 ChangeFormSetting 並存或取代）。

### S4. 搬移（核心）
- FormGetMove( nIndex )：預覽外框拖曳 到 放開反算相對座標（垂直用 nEffTop/nEffCenter、水平用 nEffCenter/nEffLeft）到 REPLACE SubForm2（該 RecNo）到 GetList:={} 到 InitialForm 重畫。比照 ChangeFormSetting MoveSubBox 的寫回，只是來源改滑鼠。

### S5. 屬性動作抽函式共用
- 把 ChangeFormSetting 的 EditGetWidth/EditPicture/EditWhenFun/EditValidFun/EditGetEnable/Delete 抽成可獨立呼叫小函式，讓「底部功能列」「雙擊選單」「原 Alt+F 選單」三者共用。

### S6. 改寬度（SIZE）滑鼠（可延後）
- GET 右緣拖曳改 NGETWIDTH。

### S7. 相容 / 收尾
- 80x25、非編輯模式 100% 零回歸（滑鼠不介入輸入）。
- 與本次 splitter/欄位 drag 共存（不同觸發、不同模式）。
- UpdateList、PDF 報告、Memory。

---

## 五、風險與注意事項
1. 輸入 vs 編輯衝突：務必用明確模式開關隔離（S3）。成敗關鍵。
2. 相對座標基準：搬移反算務必用 nEffCenter/nEffTop/nEffLeft（依 bit0），與 InitialForm 一致，否則位移錯位。
3. BOX 模式（nCtrlFlag=BOX_FLAG）欄位用 NTOP/NLEFT（非 NSAYROW/NSAYCOL），需分開處理。
4. 不碰全域物件：GET 是單純表單欄位（存 SubForm2），非 THREE/FOUR 那種啟動期全域子視窗，無爆版/腐蝕風險。
5. 右鍵不可用（已綁 ESC）：功能選單改雙擊 + 底部可點功能列。
6. 多站共用 SubForm2：改的是版面定義，影響所有站該表單（與 Alt+F 現況相同，非新風險）。

---

## 六、待討論點（等你回來）
1. 進入編輯模式的觸發鍵：沿用 Alt+F？還是新熱鍵、Alt+F 保留鍵盤版？
2. 屬性選單主用「底部可點功能列」還是「雙擊彈出」？（我建議底部功能列為主）
3. 搬移是「SAY+GET 一起動」還是可分開搬？
4. 改寬用「拖右緣」還是只放功能列選單？
5. 功能列要不要含「新增欄位/刪除欄位」？（涉及 CORDER 重排，較複雜）
6. 是否要「格線吸附/對齊輔助」讓排版整齊？
