# 機能設計書 9-タブの移動

## 概要

本ドキュメントは、Windows Terminal における「タブの移動」機能の設計を記述する。タブの位置を前後に移動する、または別のウィンドウへタブを移動する機能の詳細を定義する。

### 本機能の処理概要

**業務上の目的・背景**：複数のタブを使って作業する際、タブの並び順を整理したい場面がある。この機能は、タブの位置を前後に移動することで、ユーザーが自分の作業スタイルに合ったタブ配置を実現できるようにする。さらに、別のウィンドウへタブを移動する機能により、マルチウィンドウ間でのタブ管理も可能にする。

**機能の利用シーン**：タブの並び順を変更したい場合にキーボードショートカットやコマンドパレットから実行される。また、タブを別のターミナルウィンドウに移動させたい場合に、ウィンドウIDを指定して実行される。ドラッグ&ドロップによるタブ移動は別のイベントハンドラで処理される。

**主要な処理内容**：
1. MoveTab アクションの受信と引数（MoveTabArgs）の解析
2. Window パラメータの確認
3. ウィンドウ指定がある場合: タブを別ウィンドウへ移動（デタッチ・コンテンツ移動・削除）
4. ウィンドウ指定がない場合: タブの位置を Direction に従って前後に移動
5. アクセシビリティ通知の発行

**関連システム・外部連携**：別ウィンドウへの移動時は、ウィンドウ間通信（_MoveContent）を使用する。

**権限による制御**：特別な権限制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | TerminalPage（メイン画面） | 主画面 | _HandleMoveTab によりタブ位置を移動 |
| 2 | TabRowControl（タブ行） | 補助画面 | TabView の CanReorderTabs=True によりドラッグ&ドロップでタブ移動 |

## 機能種別

ナビゲーション操作（タブ位置の変更） / ウィンドウ間操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| Direction | MoveTabDirection | Yes | タブの移動方向（Forward / Backward / None） | None の場合は Window が指定されていること |
| Window | String | No | 移動先のウィンドウID。"new" で新しいウィンドウ、数字で既存ウィンドウID、名前でウィンドウ名 | 空文字列の場合はタブ行内移動 |

### 入力データソース

- アクション引数: MoveTabArgs 経由の Direction と Window
- フォーカスタブ: _senderOrFocusedTab(sender) で取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| (なし) | - | タブの位置が変更される、または別ウィンドウに移動される |

### 出力先

- タブ行内移動: TabView 内のタブ位置が変更
- ウィンドウ間移動: タブが元ウィンドウから削除され、指定ウィンドウに追加

## 処理フロー

### 処理シーケンス

```
1. _HandleMoveTab がアクションイベントを受信
   └─ MoveTabArgs から Direction と Window を取得
2. _MoveTab の呼び出し
   └─ タブが null の場合は false を返却
3. ウィンドウ指定の確認
   └─ Window が空でない場合:
      a. 自ウィンドウと同一か確認（同一なら何もしない）
      b. BuildStartupActions でタブの状態をシリアライズ
      c. _DetachTabFromWindow でタブをデタッチ
      d. _MoveContent でコンテンツを移動先ウィンドウに送信
      e. _RemoveTab で元ウィンドウからタブを削除
      f. アクセシビリティ通知を発行
4. タブ行内移動（Direction が None でない場合）:
   └─ タブの現在インデックスを取得
   └─ delta を計算（Forward: +1, Backward: -1）
   └─ _TryMoveTab で位置を変更
```

### フローチャート

```mermaid
flowchart TD
    A[_HandleMoveTab 受信] --> B[MoveTabArgs 取得]
    B --> C[_MoveTab 呼び出し]
    C --> D{タブが null?}
    D -->|Yes| E[false 返却]
    D -->|No| F{Window が指定?}
    F -->|Yes| G{自ウィンドウと同一?}
    G -->|Yes| H[何もしない true返却]
    G -->|No| I[BuildStartupActions]
    I --> J[_DetachTabFromWindow]
    J --> K[_MoveContent]
    K --> L[_RemoveTab]
    L --> M[アクセシビリティ通知]
    F -->|No| N{Direction != None?}
    N -->|Yes| O[delta 計算]
    O --> P[_TryMoveTab]
    N -->|No| Q[true 返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | ウィンドウ指定優先 | Window パラメータが指定されている場合、タブ行内移動ではなくウィンドウ間移動を行う | Window が空でない場合 |
| BR-02 | 自ウィンドウへの移動は無操作 | 移動先ウィンドウが現在のウィンドウと同一の場合、何もせず true を返す | windowId == WindowName or WindowId の場合 |
| BR-03 | 位置クランプ | タブ行内移動時、移動先インデックスは [0, _tabs.Size()-1] にクランプされる | 常時 |
| BR-04 | インデックス更新 | タブ移動後、全タブのインデックスを _UpdateTabIndices で更新する | 常時 |
| BR-05 | アクセシビリティ通知 | ウィンドウ間移動時、移動先に応じた通知メッセージを発行する | ウィンドウ間移動時 |

### 計算ロジック

移動先インデックスの算出（タブ行内移動）：
- `delta = (direction == MoveTabDirection::Forward) ? 1 : -1`
- `suggestedNewTabIndex = currentTabIndex + delta`
- `newTabIndex = std::clamp(suggestedNewTabIndex, 0, _tabs.Size() - 1)`

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

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

本機能はデータベースを使用しない。

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | データベース操作なし |

### テーブル別操作詳細

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| false | タブ不在 | 移動対象のタブが null | false を返却しアクションを未処理にする |

### リトライ仕様

リトライは行わない。

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

ウィンドウ間移動は、デタッチ→コンテンツ送信→削除の一連の処理として実行される。途中で失敗した場合の部分ロールバックは実装されていない。

## パフォーマンス要件

タブ行内移動は即座に実行される。ウィンドウ間移動はプロセス間通信を含むため、タブ行内移動より遅延が大きい可能性がある。

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

ウィンドウ間のタブ移動では、タブのセッション状態（シェルプロセス接続等）が別ウィンドウに転送される。同一アプリケーション内での移動であるため、特別なセキュリティリスクはない。

## 備考

- ドラッグ&ドロップによるタブ移動は _TabDragStarted / _TabDragCompleted イベントハンドラで別途処理される（TabManagement.cpp 1116-1148行目）
- Window パラメータに "new" を指定すると新しいウィンドウが作成される
- アクセシビリティ通知は AutomationNotificationKind::ActionCompleted で発行される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | MoveTabArgs の定義（341-346行目）。MoveTabDirection 型の Direction と String 型の Window プロパティ |

**読解のコツ**: MoveTabArgs は Direction と Window の2つのパラメータを持つ。Window が空でない場合はウィンドウ間移動、それ以外は Direction に従ったタブ行内移動となる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AppActionHandlers.cpp | `src/cascadia/TerminalApp/AppActionHandlers.cpp` | _HandleMoveTab（855-863行目）がエントリーポイント |

**主要処理フロー**:
1. **855-856行目**: _HandleMoveTab の定義
2. **858行目**: MoveTabArgs へのキャスト
3. **860行目**: `_MoveTab(_senderOrFocusedTab(sender), realArgs)` 呼び出し
4. **861行目**: 戻り値で Handled を設定

#### Step 3: タブ移動ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | _MoveTab（2531-2594行目） |

**主要処理フロー**:
- **2533-2536行目**: タブの null チェック
- **2540行目**: `args.Window()` でウィンドウIDを取得
- **2541-2576行目**: ウィンドウ指定がある場合のウィンドウ間移動処理
- **2544-2548行目**: 自ウィンドウとの一致チェック
- **2552-2555行目**: BuildStartupActions、_DetachTabFromWindow、_MoveContent、_RemoveTab
- **2578-2591行目**: Direction による行内移動処理
- **2583-2584行目**: タブインデックスの取得
- **2588-2589行目**: delta 計算と _TryMoveTab 呼び出し

#### Step 4: タブ行内移動の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TabManagement.cpp | `src/cascadia/TerminalApp/TabManagement.cpp` | _TryMoveTab（1089-1114行目） |

**主要処理フロー**:
- **1092行目**: `std::clamp` で新しいインデックスをクランプ
- **1093-1103行目**: タブの削除と新位置への挿入
- **1105-1112行目**: アクセシビリティ通知の発行

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

```
_HandleMoveTab (AppActionHandlers.cpp:855)
    |
    +-- _MoveTab (TerminalPage.cpp:2531)
            |
            +-- [Window 指定あり]
            |       |
            |       +-- BuildStartupActions() -- タブ状態のシリアライズ
            |       +-- _DetachTabFromWindow() -- タブのデタッチ
            |       +-- _MoveContent() -- コンテンツの移動先送信
            |       +-- _RemoveTab() -- 元ウィンドウから削除
            |       +-- RaiseNotificationEvent() -- アクセシビリティ通知
            |
            +-- [Direction 指定]
                    |
                    +-- _GetTabIndex() -- 現在インデックス取得
                    +-- _TryMoveTab (TabManagement.cpp:1089)
                            |
                            +-- std::clamp() -- 新インデックスクランプ
                            +-- _tabs.RemoveAt() / InsertAt() -- 位置変更
                            +-- _tabView.TabItems().RemoveAt() / InsertAt()
                            +-- _UpdateTabIndices()
                            +-- RaiseNotificationEvent() -- アクセシビリティ通知
```

### データフロー図

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

MoveTabArgs -----------> _HandleMoveTab
  Direction               |
  Window                _MoveTab
                          |
                        +-[Window指定]-> デタッチ -> _MoveContent -> 別ウィンドウ
                        |                                          元ウィンドウから削除
                        |
                        +-[Direction指定]-> delta計算 -> _TryMoveTab -> タブ位置変更
                                                                        インデックス更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AppActionHandlers.cpp | `src/cascadia/TerminalApp/AppActionHandlers.cpp` | ソース | _HandleMoveTab エントリーポイント |
| TerminalPage.cpp | `src/cascadia/TerminalApp/TerminalPage.cpp` | ソース | _MoveTab の実装（ウィンドウ間移動含む） |
| TabManagement.cpp | `src/cascadia/TerminalApp/TabManagement.cpp` | ソース | _TryMoveTab の実装（行内移動） |
| TerminalPage.h | `src/cascadia/TerminalApp/TerminalPage.h` | ヘッダー | TerminalPage クラスのメソッド宣言 |
| ActionArgs.idl | `src/cascadia/TerminalSettingsModel/ActionArgs.idl` | IDL | MoveTabArgs の WinRT インターフェース定義 |
