# 機能設計書 17-ペインの分割

## 概要

本ドキュメントは、Windows Terminalにおける現在のタブを水平または垂直に分割し、新しいペインを作成する機能（SplitPane アクション）の設計を記述する。

### 本機能の処理概要

現在のタブ内を分割して新しいターミナルペインを作成する機能である。分割方向（水平/垂直/自動）、分割サイズ（比率）、新しいペインに使用するプロファイル/コンテンツ、および分割モード（手動/複製）を指定できる。分割前にスペースの十分さを事前計算し、表示可能な場合のみ分割を実行する。

**業務上の目的・背景**：1つのタブ内で複数のターミナルセッションを同時に表示することにより、開発者はコード編集、ビルド、テスト実行などを同時に確認しながら作業できる。タブ切り替えなしで複数の出力を並列に監視できるため、生産性が向上する。

**機能の利用シーン**：開発者がサーバーログを監視しながらコマンドを実行する場面、複数のディレクトリで同時に作業する場面、テストの出力と実装コードを同時に確認する場面などで使用される。

**主要な処理内容**：
1. SplitPaneアクションの受信とパラメータの取得
2. プロファイルインデックスの有効性チェック
3. 複製モード（Duplicate）の場合、現在のタブからの設定取得
4. _MakePane によるペインオブジェクトの生成
5. 分割可能性の事前計算（PreCalculateCanSplit）
6. ズーム解除（_UnZoomIfNeeded）
7. Tab::SplitPane による実際の分割処理
8. 新しいペインへのフォーカス設定

**関連システム・外部連携**：ConPTY接続（新しいペインでシェルプロセスを起動する際に使用）。

**権限による制御**：権限による制御なし。ただし、分割先が昇格プロファイルの場合は別ウィンドウで処理される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | TerminalPage（メイン画面） | 主画面 | _HandleSplitPaneによりタブを水平/垂直分割 |

## 機能種別

UI操作 / ペイン構造変更

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| SplitDirection | SplitDirection enum | Yes | 分割方向（Automatic, Up, Right, Down, Left） | なし |
| SplitSize | float | No | 分割比率（0.0-1.0、デフォルト0.5） | (0, 1) の範囲 |
| SplitMode | SplitType enum | No | 分割モード（Manual, Duplicate） | なし |
| ContentArgs | INewContentArgs | No | 新しいペインのコンテンツ引数（プロファイル指定等） | ProfileIndex の範囲チェック |

### 入力データソース

キーバインド、コマンドパレット、またはプログラム的なアクション実行によりSplitPaneArgsとして渡される。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 新しいペイン | Pane | 作成された新しいペインオブジェクト |
| 分割レイアウト | Grid | 更新されたペインのグリッドレイアウト |

### 出力先

現在のタブ内のペインツリー（Paneの二分木構造）

## 処理フロー

### 処理シーケンス

```
1. _HandleSplitPane が呼び出される
   └─ ActionEventArgs から SplitPaneArgs を取得
2. プロファイルインデックスの有効性チェック
   └─ _shouldBailForInvalidProfileIndex でインデックス範囲を確認
3. 複製モード判定
   └─ SplitMode == Duplicate の場合、現在のタブの設定を取得
4. 対象タブの特定
   └─ _senderOrFocusedTab(sender)
5. _MakePane で新しいペインを生成
   └─ ContentArgs と duplicateFromTab を渡す
6. _SplitPane を呼び出し
   ├─ タブが null かつタブ数が 0 の場合、新規タブとして作成
   ├─ 設定タブの場合、分割を行わない
   ├─ ペインが null（昇格プロファイル等）の場合、分割を行わない
   ├─ 利用可能スペースの計算
   ├─ PreCalculateCanSplit で分割可能性を事前チェック
   ├─ _UnZoomIfNeeded でズーム解除
   └─ activeTab->SplitPane で実際の分割実行
7. 新しいペインにフォーカスを設定
```

### フローチャート

```mermaid
flowchart TD
    A[SplitPane アクション受信] --> B[SplitPaneArgs 取得]
    B --> C{プロファイルインデックス有効?}
    C -->|No| D[Handled = false]
    C -->|Yes| E{SplitMode == Duplicate?}
    E -->|Yes| F[現在のタブ設定を取得]
    E -->|No| G[対象タブ特定]
    F --> G
    G --> H[_MakePane でペイン生成]
    H --> I[_SplitPane 呼び出し]
    I --> J{タブが null?}
    J -->|Yes, タブ数0| K[新規タブとして作成]
    J -->|No| L{設定タブ?}
    L -->|Yes| M[何もしない]
    L -->|No| N{ペインが null?}
    N -->|Yes| O[何もしない]
    N -->|No| P[分割可能性チェック]
    P --> Q{分割可能?}
    Q -->|No| R[何もしない]
    Q -->|Yes| S[ズーム解除]
    S --> T[SplitPane 実行]
    T --> U[新ペインにフォーカス]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-17-01 | プロファイルインデックス検証 | ProfileIndex が ActiveProfiles の範囲外の場合、アクションを処理しない | NewTerminalArgs で ProfileIndex が指定された場合 |
| BR-17-02 | 複製モード | SplitMode が Duplicate の場合、現在のタブと同じプロファイル設定で新しいペインを作成する | SplitMode == SplitType::Duplicate |
| BR-17-03 | 自動分割方向 | SplitDirection が Automatic の場合、ペインの現在のサイズに基づき幅>=高さならVertical、そうでなければHorizontalとする | SplitDirection == Automatic |
| BR-17-04 | 分割可能性チェック | 分割後の各ペインが最小サイズを満たせない場合、分割を実行しない | PreCalculateCanSplit が nullopt を返す場合 |
| BR-17-05 | 設定タブの分割禁止 | 設定タブは分割できない | activeTab == _settingsTab |
| BR-17-06 | タブなし時の昇格 | 分割時にタブが存在しない場合（スタートアップ時の特殊ケース）、新規タブとして作成する | tab == nullptr && _tabs.Size() == 0 |
| BR-17-07 | ズーム解除 | 分割前にズーム状態を解除する | ペインがズームされている場合 |

### 計算ロジック

- 分割比率: SplitSize パラメータで指定（デフォルト0.5）
- 自動分割方向: actualSize.Width >= actualSize.Height ? Vertical : Horizontal

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

データベース操作なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| なし | 無効プロファイルインデックス | ProfileIndexがActiveProfiles範囲外 | Handled(false) を設定し処理スキップ |
| なし | 分割不可 | スペース不足で分割できない | 分割せず処理を終了 |
| なし | ペイン生成失敗 | _MakePane が nullptr を返した場合（昇格プロファイル等） | 分割せず処理を終了 |

### リトライ仕様

リトライ不要。

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

トランザクション管理なし。

## パフォーマンス要件

分割処理は即座に実行されること。新しいシェルプロセスの起動にはConPTY接続のオーバーヘッドがあるが、UI側の分割は同期的に完了する。

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

昇格プロファイル（管理者権限）が指定された場合、現在のウィンドウ内では処理せず、別の昇格ウィンドウに処理を委譲する。

## 備考

- ペインのツリー構造は二分木で表現され、各ノードは SplitState（Horizontal/Vertical/None）を持つ
- 分割時にはイベントハンドラのアタッチ、ペインIDの割り当てが行われる
- GH#6586: 分割後の新しいペインは自動的にフォーカスされず、手動でフォーカスを設定する必要がある

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | **71-84行目**: SplitDirection / SplitType enum の定義 |
| 1-2 | ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | **262-272行目**: SplitPaneArgs の定義。SplitDirection, ContentArgs, SplitMode, SplitSize プロパティ |
| 1-3 | Pane.h | `src/cascadia/TerminalApp/Pane.h` | **48-53行目**: SplitState enum（None, Horizontal, Vertical）。**62-72行目**: Pane クラスのコンストラクタ（leaf と branch） |

**読解のコツ**: Pane は二分木構造で、leaf ノードがコンテンツを持ち、branch ノードが2つの子ペインを持つ。SplitDirection はアクション引数の方向指定、SplitState はペインの内部状態を表す。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AppActionHandlers.cpp | `src/cascadia/TerminalApp/AppActionHandlers.cpp` | **268-294行目**: _HandleSplitPane の実装 |

**主要処理フロー**:
1. **275行目**: SplitPaneArgs を取得
2. **277-281行目**: プロファイルインデックスの有効性チェック
3. **283行目**: 複製モードの場合、現在のタブを取得
4. **285行目**: _senderOrFocusedTab で対象タブを特定
5. **287-291行目**: _SplitPane を呼び出し（分割方向、サイズ、新ペイン）

#### Step 3: 分割処理の実装

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | **2693-2764行目**: _SplitPane の実装。タブ null 時の特殊処理、設定タブの制限、ペイン null チェック、分割可能性チェック、実際の分割実行 |
| 3-2 | Tab.cpp | `src/cascadia/TerminalApp/Tab.cpp` | **616-636行目**: Tab::SplitPane の実装。イベントハンドラのアタッチとペインIDの割り当て |
| 3-3 | Pane.cpp | `src/cascadia/TerminalApp/Pane.cpp` | **2174-2193行目**: Pane::Split の実装。再帰的にアクティブペインを検索し、_Split を呼び出す |

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

```
TerminalPage::_HandleSplitPane (AppActionHandlers.cpp:268)
    |
    +-- _shouldBailForInvalidProfileIndex(settings, ContentArgs)
    |
    +-- _senderOrFocusedTab(sender) でタブ特定
    |
    +-- _MakePane(ContentArgs, duplicateFromTab) でペイン生成
    |
    +-- TerminalPage::_SplitPane(tab, direction, size, newPane) (TerminalPage.cpp:2693)
            |
            +-- PreCalculateCanSplit() で分割可能性チェック
            |
            +-- _UnZoomIfNeeded() でズーム解除
            |
            +-- Tab::SplitPane(splitType, splitSize, newPane) (Tab.cpp:616)
                    |
                    +-- pane->WalkTree() でイベントハンドラアタッチ & ID割当
                    |
                    +-- Pane::Split(splitType, splitSize, newPane) (Pane.cpp:2174)
                            |
                            +-- _Split(splitType, splitSize, newPane)
```

### データフロー図

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

SplitPaneArgs          TerminalPage                        ペインツリー
(Direction, Size,  --> ::_HandleSplitPane() ----------->  (二分木更新)
 Mode, ContentArgs)        |                                   |
                           v                                   v
                     _MakePane() --> 新Pane生成          Grid レイアウト更新
                           |                                   |
                     _SplitPane() --> Tab::SplitPane()    新ペインにフォーカス
                           |
                     Pane::Split() --> _Split()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AppActionHandlers.cpp | `src/cascadia/TerminalApp/AppActionHandlers.cpp` | ソース | _HandleSplitPane の実装 |
| TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | ソース | _SplitPane / _MakePane の実装 |
| TerminalPage.h | `src/cascadia/TerminalApp/TerminalPage.h` | ヘッダー | TerminalPage の宣言 |
| Tab.cpp | `src/cascadia/TerminalApp/Tab.cpp` | ソース | Tab::SplitPane の実装 |
| Tab.h | `src/cascadia/TerminalApp/Tab.h` | ヘッダー | Tab クラスの宣言 |
| Pane.cpp | `src/cascadia/TerminalApp/Pane.cpp` | ソース | Pane::Split / _Split の実装 |
| Pane.h | `src/cascadia/TerminalApp/Pane.h` | ヘッダー | Pane クラスの宣言 |
| ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | IDL | SplitPaneArgs / SplitDirection / SplitType の型定義 |
