# 機能設計書 23-ペインの入れ替え

## 概要

本ドキュメントは、Windows Terminal における「ペインの入れ替え（SwapPane）」機能の設計を記述する。現在フォーカスされているペインと、指定方向に隣接するペインの位置を入れ替える機能である。

### 本機能の処理概要

**業務上の目的・背景**：複数ペインでの作業において、ペインの配置を後から変更したい場合がある。特に、分割操作の順序によって意図しない配置になった場合に、ペインの内容を保持したまま位置だけを入れ替えることで、ワークスペースの最適化が可能になる。

**機能の利用シーン**：ユーザーが左右に2つのペインを表示しており、左側のペインの内容を右側に、右側のペインの内容を左側に入れ替えたい場合に利用する。キーバインドで方向を指定して即座にスワップできる。

**主要な処理内容**：
1. SwapPaneアクション引数から入れ替え方向（FocusDirection）を取得する
2. ズーム状態の場合は事前にアンズームする
3. ペインツリーを走査して指定方向の隣接ペインを特定する
4. 2つのペインの位置（ツリー構造内の親子関係）を入れ替える
5. フォーカスは元のペインに維持される

**関連システム・外部連携**：特になし。

**権限による制御**：特になし。ただしParent/Child方向のスワップは循環参照を防ぐため禁止されている。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | TerminalPage（メイン画面） | 主画面 | _HandleSwapPaneによりペイン位置入れ替え |

## 機能種別

UI操作（ペインレイアウト変更）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| Direction | FocusDirection (enum) | Yes | 入れ替え方向 | None/Parent/Child 以外であること |

### 入力データソース

キーバインド設定またはコマンドライン。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Handled | bool | 入れ替えが成功したかどうか |

### 出力先

UIレイアウト更新（ペイン位置の入れ替え）。

## 処理フロー

### 処理シーケンス

```
1. _HandleSwapPane が ActionEventArgs を受信
   └─ SwapPaneArgs にキャストして Direction を取得
2. Direction が None の場合は処理をスキップ（Handled=false）
3. _SwapPane(direction) を呼び出し
   └─ フォーカスされたタブの TabImpl を取得
   └─ _UnZoomIfNeeded() でズームを解除
4. Tab::SwapPane(direction) を呼び出し
   └─ Parent/Child方向の場合はfalseを返す（循環参照防止）
   └─ _rootPane->NavigateDirection() で隣接ペインを検索
5. 隣接ペインが見つかった場合
   └─ _rootPane->SwapPanes(activePane, neighbor) でスワップ実行
6. 結果を Handled に設定
```

### フローチャート

```mermaid
flowchart TD
    A[SwapPane アクション発火] --> B{Direction == None?}
    B -->|Yes| C[Handled = false]
    B -->|No| D[_SwapPane 呼び出し]
    D --> E[_UnZoomIfNeeded]
    E --> F[Tab::SwapPane 呼び出し]
    F --> G{Parent/Child方向?}
    G -->|Yes| H[false を返す]
    G -->|No| I[NavigateDirection で隣接ペイン検索]
    I --> J{隣接ペインが見つかったか?}
    J -->|Yes| K[SwapPanes で位置入れ替え]
    J -->|No| L[false を返す]
    K --> M[Handled = true]
    C --> N[終了]
    H --> N
    L --> N
    M --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-23-01 | Parent/Childスワップ禁止 | 循環参照を防ぐため、Parent/Child方向のスワップは拒否される | direction == Parent or Child |
| BR-23-02 | ズーム事前解除 | スワップ前にズーム状態を解除する | ズーム中 |
| BR-23-03 | 循環参照チェック | 一方のペインが他方のペインの子である場合はスワップしない | ペイン間に親子関係がある場合 |
| BR-23-04 | リーフノードチェック | ペインツリーがリーフ（単一ペイン）の場合はスワップしない | ルートペインがリーフ |

### 計算ロジック

隣接ペインの特定はMoveFocusと同じNavigateDirectionアルゴリズムを使用する。スワップはペインツリー内で親ペインの子ポインタを入れ替えることで実現する。

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無操作 | Direction が None | Handled=false |
| - | スワップ拒否 | Parent/Child方向の指定 | false を返す |
| - | スワップ失敗 | 隣接ペインが存在しない | false を返す |
| - | スワップ失敗 | ペイン間に循環参照がある | false を返す |

### リトライ仕様

リトライは不要。

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

ペインのスワップはアトミックに実行される（ツリー内のポインタ入れ替え）。

## パフォーマンス要件

ペインツリーの走査とポインタ入れ替えは即座に完了する。

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

特になし。

## 備考

- SwapPaneアクションとMoveFocusアクションは同じFocusDirection列挙型を使用するが、Parent/Child方向はSwapPaneでは無効。
- コマンドラインの `wt swap-pane` サブコマンドからも呼び出し可能。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | **244-248行目**: SwapPaneArgs クラスの定義。Direction (FocusDirection) プロパティを持つ |
| 1-2 | ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | **56-69行目**: FocusDirection 列挙型（MoveFocusと共通） |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AppActionHandlers.cpp | `src/cascadia/TerminalApp/AppActionHandlers.cpp` | **529-544行目**: `_HandleSwapPane` メソッド |

**主要処理フロー**:
1. **532行目**: `SwapPaneArgs` にキャスト
2. **534行目**: `Direction == None` のチェック
3. **541行目**: `_SwapPane(realArgs.Direction())` の呼び出し

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | **2115-2123行目**: `_SwapPane` メソッド。ズーム解除後にTab::SwapPaneを呼び出す |

**主要処理フロー**:
- **2119行目**: `_UnZoomIfNeeded()` でズームを解除
- **2120行目**: `tabImpl->SwapPane(direction)` をタブに委譲

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Tab.cpp | `src/cascadia/TerminalApp/Tab.cpp` | **897-920行目**: `SwapPane` メソッド。Parent/Childチェック後にNavigateDirectionとSwapPanesを呼び出す |

**主要処理フロー**:
- **903行目**: Parent/Child方向のチェック
- **909行目**: `NavigateDirection` で隣接ペインを検索
- **914行目**: `SwapPanes` でスワップ実行

#### Step 5: Pane層のスワップ実装

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | Pane.cpp | `src/cascadia/TerminalApp/Pane.cpp` | **585-595行目**: `SwapPanes` メソッド。同一ペイン/リーフ/循環参照のチェック後にツリー内でペインを入れ替える |

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

```
TerminalPage::_HandleSwapPane (AppActionHandlers.cpp:529)
    |
    +-- TerminalPage::_SwapPane (TerminalPage.cpp:2115)
            |
            +-- TerminalPage::_UnZoomIfNeeded
            |
            +-- Tab::SwapPane (Tab.cpp:897)
                    |
                    +-- Pane::NavigateDirection (Pane.cpp:343)
                    |
                    +-- Pane::SwapPanes (Pane.cpp:585)
                            |
                            +-- Pane::_HasChild (循環参照チェック)
```

### データフロー図

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

SwapPaneArgs ──> Direction ──> _HandleSwapPane
                                    |
                            _UnZoomIfNeeded
                                    |
                           Tab::SwapPane
                                    |
                    NavigateDirection ──> neighbor (Pane*)
                                    |
                    SwapPanes(activePane, neighbor)
                                    |
                           ペインツリー内のポインタ入れ替え ──> UI更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AppActionHandlers.cpp | `src/cascadia/TerminalApp/AppActionHandlers.cpp` | ソース | _HandleSwapPane アクションハンドラ |
| TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | ソース | _SwapPane メソッドの実装 |
| Tab.cpp | `src/cascadia/TerminalApp/Tab.cpp` | ソース | SwapPane メソッドの実装 |
| Pane.cpp | `src/cascadia/TerminalApp/Pane.cpp` | ソース | SwapPanes, NavigateDirection の実装 |
| Pane.h | `src/cascadia/TerminalApp/Pane.h` | ヘッダー | Pane クラスの宣言 |
| ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | IDL | SwapPaneArgs の型定義 |
| ActionArgs.cpp | `src/cascadia/TerminalSettingsModel/ActionArgs.cpp` | ソース | SwapPaneArgs::GenerateName の実装 |
| AppCommandlineArgs.cpp | `src/cascadia/TerminalApp/AppCommandlineArgs.cpp` | ソース | swap-pane コマンドラインサブコマンドの解析 |
