# 機能設計書 21-フォーカスの移動

## 概要

本ドキュメントは、Windows Terminal における「フォーカスの移動（MoveFocus）」機能の設計を記述する。ペイン間でフォーカスを上下左右に移動し、MRU（Most Recently Used）順や親子関係によるナビゲーションも可能にする機能である。

### 本機能の処理概要

**業務上の目的・背景**：ターミナルを複数のペインに分割して作業する場合、キーボード操作のみでペイン間を素早く移動できることが生産性向上に不可欠である。マウス操作なしにフォーカスを移動できることで、コマンドライン中心の作業フローを維持できる。

**機能の利用シーン**：ユーザーがターミナルウィンドウを複数ペインに分割し、異なるペインで異なるタスク（ログ監視、コード編集、テスト実行など）を並行して行う際に、キーバインドを使用してペイン間のフォーカスを切り替える場面で利用される。

**主要な処理内容**：
1. ユーザーがキーバインドを押下すると、MoveFocusアクションが発火する
2. アクション引数から移動方向（Left/Right/Up/Down/Previous/PreviousInOrder/NextInOrder/First/Parent/Child）を取得する
3. ペインツリーを走査し、指定方向に隣接するペインを特定する
4. 特定されたペインにフォーカスを移動し、ズーム状態の場合はズーム対象を更新する
5. 移動が成功した場合のみアクションをHandledとしてマークし、失敗時はキーバインドがターミナルに伝播する

**関連システム・外部連携**：特になし。TerminalApp内部のペイン管理機構で完結する。

**権限による制御**：特になし。すべてのペインに対して等しくフォーカス移動が可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | TerminalPage（メイン画面） | 主画面 | _HandleMoveFocusによりペイン間フォーカス移動 |

## 機能種別

UI操作（ペインフォーカス制御）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| FocusDirection | FocusDirection (enum) | Yes | フォーカスの移動方向 | None以外の値であること（None の場合は何もしない） |

### FocusDirection 列挙値

| 値 | 説明 |
|-----|------|
| None | 方向なし（無操作） |
| Left | 左方向 |
| Right | 右方向 |
| Up | 上方向 |
| Down | 下方向 |
| Previous | MRU順で前のペイン |
| PreviousInOrder | ツリー順で前のペイン |
| NextInOrder | ツリー順で次のペイン |
| First | 最初のペイン |
| Parent | 親ペイン |
| Child | 子ペイン |

### 入力データソース

キーバインド設定（settings.json のアクション定義）からのアクション発火。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Handled | bool | アクションが正常に処理されたかどうか |

### 出力先

UIフォーカス状態の更新（指定ペインへのフォーカス移動）。

## 処理フロー

### 処理シーケンス

```
1. _HandleMoveFocus が ActionEventArgs を受信
   └─ MoveFocusArgs にキャストして FocusDirection を取得
2. FocusDirection が None の場合は処理をスキップ（Handled=false）
   └─ キーバインドがターミナルに伝播する
3. _MoveFocus(direction) を呼び出し
   └─ フォーカスされたタブの TabImpl を取得
4. TabImpl::NavigateFocus(direction) を呼び出し
   └─ _rootPane->NavigateDirection() でペインツリーを走査
5. 隣接ペインが見つかった場合
   └─ _rootPane->FocusPane(newFocus) でフォーカスを設定
6. ズーム状態の場合は UpdateZoom(newFocus) で表示を更新
7. 移動成功/失敗の結果を Handled に設定
```

### フローチャート

```mermaid
flowchart TD
    A[MoveFocus アクション発火] --> B{FocusDirection == None?}
    B -->|Yes| C[Handled = false]
    B -->|No| D[_MoveFocus 呼び出し]
    D --> E[Tab::NavigateFocus 呼び出し]
    E --> F[Pane::NavigateDirection でペイン検索]
    F --> G{隣接ペインが見つかったか?}
    G -->|Yes| H[FocusPane で新ペインにフォーカス設定]
    H --> I{ズーム中か?}
    I -->|Yes| J[UpdateZoom で表示更新]
    I -->|No| K[Handled = true]
    J --> K
    G -->|No| L[Handled = false]
    C --> M[終了]
    K --> M
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-21-01 | 方向なし時の無操作 | FocusDirection が None の場合、アクションは未処理としてマークされる | 常時 |
| BR-21-02 | 移動失敗時の伝播 | 移動先ペインが存在しない場合、キーバインドがターミナルに伝播する（GH#6129） | ペインが1つのみの場合等 |
| BR-21-03 | ズーム時の追従 | ズーム中にフォーカスを移動した場合、ズーム対象が新しいフォーカスペインに変更される | ズーム状態中 |
| BR-21-04 | MRU順ナビゲーション | Previous 方向の場合、最近使用した順でペインを走査する | direction == Previous |

### 計算ロジック

方向ベースのナビゲーションでは、ペインツリーの物理的配置（座標）を基に最も近い隣接ペインを特定する。分割方向と移動方向が一致する場合は子ペイン間を移動し、一致しない場合は再帰的に子ペインに処理を委譲する。

## データベース操作仕様

### 操作別データベース影響一覧

該当なし（データベースを使用しない）。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無操作 | FocusDirection が None | アクション未処理（Handled=false）として返す |
| - | 移動失敗 | 指定方向にペインが存在しない | アクション未処理として返し、キー入力をターミナルに伝播 |
| - | タブ不在 | フォーカスされたタブが存在しない | false を返す |

### リトライ仕様

リトライは不要。

## トランザクション仕様

該当なし。

## パフォーマンス要件

ペインツリーの走査はO(n)（nはペイン数）であり、通常のペイン数（数十程度）では即座に完了する。

## セキュリティ考慮事項

特になし。

## 備考

- GH#6129: フォーカス移動が失敗した場合にキーバインドがターミナルに伝播する仕様は、ペインが1つのみの場合にAlt+矢印キー等のショートカットが通常通りターミナルアプリケーションに届くことを保証するための設計。
- GH#6219: 同様の設計意図。

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、アクション引数の定義とFocusDirection列挙型を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | **56-69行目**: FocusDirection 列挙型の定義（None, Left, Right, Up, Down, Previous, PreviousInOrder, NextInOrder, First, Parent, Child） |
| 1-2 | ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | **238-242行目**: MoveFocusArgs クラスの定義。FocusDirection プロパティを持つ |

**読解のコツ**: IDLファイルはWinRT/MIDLの型定義。`[default_interface]`はデフォルトインターフェースを指定するWinRT属性。

#### Step 2: エントリーポイントを理解する

アクションハンドラが処理の起点となる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AppActionHandlers.cpp | `src/cascadia/TerminalApp/AppActionHandlers.cpp` | **508-527行目**: `_HandleMoveFocus` メソッド。MoveFocusArgs から方向を取得し、_MoveFocus を呼び出す |

**主要処理フロー**:
1. **511行目**: `args.ActionArgs().try_as<MoveFocusArgs>()` で引数をキャスト
2. **513行目**: `FocusDirection::None` のチェック
3. **523行目**: `_MoveFocus(realArgs.FocusDirection())` の呼び出し
4. **524行目**: 移動成功/失敗を `args.Handled()` に設定

#### Step 3: TerminalPage層の処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | **2099-2106行目**: `_MoveFocus` メソッド。フォーカス中のタブを取得し、NavigateFocusを呼び出す |

**主要処理フロー**:
- **2101行目**: `_GetFocusedTabImpl()` でフォーカス中のタブを取得
- **2103行目**: `tabImpl->NavigateFocus(direction)` をタブに委譲

#### Step 4: Tab層の処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Tab.cpp | `src/cascadia/TerminalApp/Tab.cpp` | **865-887行目**: `NavigateFocus` メソッド。ルートペインからNavigateDirectionを呼び、FocusPaneでフォーカスを設定する |

**主要処理フロー**:
- **871行目**: `_rootPane->NavigateDirection(_activePane, direction, _mruPanes)` でナビゲーション先を検索
- **875行目**: `_rootPane->FocusPane(newFocus)` で新しいペインにフォーカス設定
- **878-881行目**: ズーム中の場合は `UpdateZoom(newFocus)` で表示を更新

#### Step 5: Pane層のナビゲーション

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | Pane.cpp | `src/cascadia/TerminalApp/Pane.cpp` | **343-421行目**: `NavigateDirection` メソッド。ペインツリーを再帰的に走査して隣接ペインを特定する |
| 5-2 | Pane.cpp | `src/cascadia/TerminalApp/Pane.cpp` | **2446-2460行目**: `FocusPane(id)` メソッド。ID指定でペインにフォーカスを設定 |

**主要処理フロー**:
- **346-349行目**: リーフノードの場合はnullptrを返す
- **426行目**: 分割方向と移動方向が一致しない場合は子ペインに再帰
- **441行目**: `_FindPaneAndNeighbor` で座標ベースの隣接ペイン検索

### プログラム呼び出し階層図

```
TerminalPage::_HandleMoveFocus (AppActionHandlers.cpp:508)
    |
    +-- TerminalPage::_MoveFocus (TerminalPage.cpp:2099)
            |
            +-- Tab::NavigateFocus (Tab.cpp:865)
                    |
                    +-- Pane::NavigateDirection (Pane.cpp:343)
                    |       |
                    |       +-- Pane::NavigateDirection (再帰)
                    |       +-- Pane::_FindPaneAndNeighbor
                    |
                    +-- Pane::FocusPane (Pane.cpp:2470)
                    |       |
                    |       +-- Pane::WalkTree
                    |       +-- Pane::_FocusFirstChild
                    |
                    +-- Tab::UpdateZoom (ズーム中のみ)
```

### データフロー図

```
[入力]                      [処理]                         [出力]

キーバインド ──> ActionEventArgs ──> _HandleMoveFocus
                                         |
MoveFocusArgs ──> FocusDirection ──> _MoveFocus
                                         |
_GetFocusedTabImpl ──> Tab ──> NavigateFocus
                                    |
_activePane + direction ──> NavigateDirection ──> newFocus (Pane*)
                                                     |
                                              FocusPane ──> UI フォーカス更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AppActionHandlers.cpp | `src/cascadia/TerminalApp/AppActionHandlers.cpp` | ソース | アクションハンドラのエントリーポイント |
| TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | ソース | _MoveFocus メソッドの実装 |
| Tab.cpp | `src/cascadia/TerminalApp/Tab.cpp` | ソース | NavigateFocus メソッドの実装 |
| Tab.h | `src/cascadia/TerminalApp/Tab.h` | ヘッダー | Tab クラスの宣言 |
| Pane.cpp | `src/cascadia/TerminalApp/Pane.cpp` | ソース | NavigateDirection, FocusPane の実装 |
| Pane.h | `src/cascadia/TerminalApp/Pane.h` | ヘッダー | Pane クラスの宣言 |
| ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | IDL | MoveFocusArgs, FocusDirection の型定義 |
| ActionArgs.cpp | `src/cascadia/TerminalSettingsModel/ActionArgs.cpp` | ソース | MoveFocusArgs::GenerateName の実装 |
| ActionAndArgs.cpp | `src/cascadia/TerminalSettingsModel/ActionAndArgs.cpp` | ソース | アクションとアクション引数のバインディング |
| ActionMap.cpp | `src/cascadia/TerminalSettingsModel/ActionMap.cpp` | ソース | キーバインドからアクションへのマッピング |
