# 通知設計書 50-UpdateSelectionMarkers

## 概要

本ドキュメントは、Windows Terminal における UpdateSelectionMarkers イベントの設計仕様を記述する。このイベントは、テキスト選択の状態変更時に発火し、選択マーカー（選択範囲の開始/終了を示すビジュアルインジケータ）の表示・非表示・位置更新を制御する。

### 本通知の処理概要

UpdateSelectionMarkersイベントは、ターミナル内のテキスト選択状態が変更されたことをUI層に通知し、選択マーカーの表示を制御するためのイベントである。ControlCoreの_updateSelectionUIメソッドから発火され、キーボード選択やマーク選択モードでの選択操作時にマーカーを表示し、マウス選択や選択解除時にマーカーを非表示にする。TermControlは受信後、UIスレッドで選択マーカーの位置計算とXAMLエレメントの表示/非表示を制御する。

**業務上の目的・背景**：テキスト選択において、キーボード選択やマークモードでは選択範囲の開始点と終了点を視覚的に示すマーカーが重要である。マーカーにより、ユーザーはどの範囲が選択されているかを明確に把握でき、選択範囲の調整が容易になる。マウス選択時にはマーカーは不要であるため、選択モードに応じた表示切り替えが行われる。

**通知の送信タイミング**：テキスト選択が開始、変更、終了された時に発火する。具体的には、TrySendKeyEvent/SendCharEvent内での選択操作、ClearSelection、ToggleBlockSelection、ToggleMarkMode、SwitchSelectionEndpoint、ExpandSelectionToWord、LeftClickOnTerminal、SelectCommand/SelectOutput、ColorSelection等の操作後に呼ばれる。

**通知の受信者**：TermControlの`_updateSelectionMarkers`ハンドラがControlCoreからのイベントを受信する。

**通知内容の概要**：UpdateSelectionMarkersEventArgsオブジェクトにClearMarkers（bool）が格納される。trueの場合はマーカーを非表示にし、falseの場合は選択範囲の位置に基づいてマーカーを表示する。

**期待されるアクション**：TermControlはUIスレッドにディスパッチし、ClearMarkersフラグに応じて選択マーカー（SelectionStartMarker/SelectionEndMarker）のVisibilityを制御する。マーカー表示時はSelectionInfoから選択範囲の座標を取得し、マーカーの位置とフリップ方向を計算する。

## 通知種別

アプリ内通知（WinRT イベント）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（UIスレッドから発火） |
| 優先度 | 中 |
| リトライ | 無し |

### 送信先決定ロジック

ControlCoreが_updateSelectionUI内でUpdateSelectionMarkersイベントを発火し、TermControlがwinrt::auto_revokeで購読する。

## 通知テンプレート

### メール通知の場合

該当なし（アプリ内イベント通知のため）

### 本文テンプレート

```
該当なし（UpdateSelectionMarkersEventArgsとしてClearMarkersフラグが伝達される）
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| ClearMarkers | マーカーを非表示にするか | SelectionMode() >= Keyboard の否定 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ユーザー操作 | キーボード入力（TrySendKeyEvent/SendCharEvent内） | 選択操作が行われた場合 | Shift+矢印キー等による選択変更 |
| ユーザー操作 | ClearSelection | 常に発火 | 選択解除 |
| ユーザー操作 | ToggleBlockSelection | 常に発火 | ブロック選択のトグル |
| ユーザー操作 | ToggleMarkMode | 常に発火 | マーク選択モードのトグル |
| ユーザー操作 | SwitchSelectionEndpoint | 常に発火 | 選択端点の切り替え |
| ユーザー操作 | ExpandSelectionToWord | 常に発火 | 単語単位の選択拡大 |
| マウス操作 | LeftClickOnTerminal | 選択変更時 | マウスクリックによる選択 |
| 内部処理 | SelectCommand/SelectOutput | 常に発火 | コマンド/出力選択 |
| 内部処理 | ColorSelection | 常に発火 | 選択範囲の色付け |
| スクロール | _ScrollPositionChanged | マーカー表示中の場合 | スクロール時のマーカー位置更新 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 特になし | _updateSelectionUIが呼び出された場合は常に発火する |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[選択操作トリガー] --> B[ControlCore::_updateSelectionUI]
    B --> C[Renderer::TriggerSelection]
    C --> D{SelectionMode >= Keyboard?}
    D -->|Yes| E[showMarkers = true]
    D -->|No| F[showMarkers = false]
    E --> G[UpdateSelectionMarkersEventArgs ClearMarkers=false]
    F --> H[UpdateSelectionMarkersEventArgs ClearMarkers=true]
    G --> I[UpdateSelectionMarkers.raise]
    H --> I
    I --> J[TermControl::_updateSelectionMarkers]
    J --> K[co_await UIスレッドへディスパッチ]
    K --> L{ClearMarkers?}
    L -->|true| M[マーカー非表示 Collapsed]
    L -->|false| N[HasSelection チェック]
    N -->|Yes| O[マーカー位置計算・表示]
    N -->|No| M
```

## データベース参照・更新仕様

### 参照テーブル一覧

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

### 更新テーブル一覧

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

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| weak_ptr失効 | TermControlがすでに破棄されている場合 | weakThis.get()のnullチェックでスキップ |
| null EventArgs | argsがnullの場合 | co_returnで早期リターン |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし

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

選択マーカー情報は純粋なUI状態であり、個人情報や機密情報を含まない。

## 備考

- ControlCore.idlではUpdateSelectionMarkersは「UIスレッドから常に呼び出される」カテゴリに分類されている
- ClearMarkersフラグはSelectionModeがKeyboard以上（Keyboard, Mark）の場合にfalse（マーカー表示）、それ以外（None, Mouse）の場合にtrue（マーカー非表示）となる
- 選択マーカーはXAMLのPathエレメント（SelectionStartMarker/SelectionEndMarker）として実装されている
- マーカーのスケーリングはFontSizeChangedイベントで設定されたScaleTransformに基づく
- マーカーのフリップ（反転）はStartAtLeftBoundary/EndAtRightBoundaryフラグにより制御される
- スクロール時にマーカーが表示中の場合、_ScrollPositionChanged内で_updateSelectionMarkersが再呼び出しされる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | EventArgs.idl | `src/cascadia/TerminalControl/EventArgs.idl` | 120-123行目: UpdateSelectionMarkersEventArgs定義（ClearMarkersプロパティ） |
| 1-2 | EventArgs.h | `src/cascadia/TerminalControl/EventArgs.h` | 183-192行目: UpdateSelectionMarkersEventArgs実装。bool ClearMarkersのWINRT_PROPERTY |
| 1-3 | ControlCore.idl | `src/cascadia/TerminalControl/ControlCore.idl` | 43-50行目: SelectionData構造体（StartPos, EndPos, Endpoint, StartAtLeftBoundary, EndAtRightBoundary） |

**読解のコツ**: ClearMarkers=trueはマーカーを非表示にする指示であり、falseはマーカーを表示する指示である。名前が否定形であることに注意。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 2193-2198行目: _updateSelectionUI - Renderer::TriggerSelection()呼び出しとUpdateSelectionMarkers.raise |

**主要処理フロー**:
1. **2195行目**: `_renderer->TriggerSelection()` でレンダラーの選択表示を更新
2. **2197行目**: `_terminal->SelectionMode() >= Keyboard` でマーカー表示の判定
3. **2198行目**: `UpdateSelectionMarkers.raise(*this, winrt::make<...>(!showMarkers))` でイベント発火

#### Step 3: 呼び出し箇所を理解する

_updateSelectionUIは多数の箇所から呼び出される。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 590-635行目: TrySendKeyEvent内の選択操作後 |
| 3-2 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 676-684行目: SendCharEvent内の選択操作後 |
| 3-3 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 1332行目: ClearSelection後 |
| 3-4 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 1378行目: ToggleBlockSelection後 |
| 3-5 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 1385行目: ToggleMarkMode後 |
| 3-6 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 1407行目: SwitchSelectionEndpoint後 |
| 3-7 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 1421行目: ExpandSelectionToWord後 |
| 3-8 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 1433行目: 選択コピー後 |
| 3-9 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 1458行目: SelectAll後 |
| 3-10 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 2098行目: LeftClickOnTerminal後 |
| 3-11 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | 2812行目: ColorSelection後 |

#### Step 4: TermControl側ハンドラ

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TermControl.cpp | `src/cascadia/TerminalControl/TermControl.cpp` | 315行目: イベント購読 |
| 4-2 | TermControl.cpp | `src/cascadia/TerminalControl/TermControl.cpp` | 3512-3551行目: _updateSelectionMarkers - UIスレッドディスパッチ、SelectionInfo取得、マーカー位置計算・表示 |
| 4-3 | TermControl.cpp | `src/cascadia/TerminalControl/TermControl.cpp` | 2506-2508行目: _ScrollPositionChanged内からの呼び出し |

**主要処理フロー**:
- **3514-3516行目**: argsのnullチェック
- **3520行目**: UIスレッドにディスパッチ
- **3523行目**: `_core.HasSelection() && !args.ClearMarkers()` の判定
- **3527行目**: `_core.SelectionInfo()` で選択範囲データを取得
- **3531-3546行目**: マーカーのフリップ方向（ScaleX）を決定
- **3549行目**: セル座標からDIP座標を計算しマーカーを配置

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

```
選択操作 (TrySendKeyEvent/ClearSelection/ToggleMarkMode 等)
    |
    +-- ControlCore::_updateSelectionUI()
            |
            +-- _renderer->TriggerSelection()
            +-- SelectionMode() >= Keyboard 判定
            +-- UpdateSelectionMarkers.raise(ClearMarkers)
                    |
                    +-- TermControl::_updateSelectionMarkers()
                            |
                            +-- co_await UIスレッド
                            +-- [ClearMarkers=true] マーカー Collapsed
                            +-- [ClearMarkers=false]
                                    +-- _core.SelectionInfo() 取得
                                    +-- displayMarker(false) [Start]
                                    +-- displayMarker(true) [End]
                                    +-- ScaleTransform フリップ
                                    +-- Canvas.SetLeft/SetTop 配置
```

### データフロー図

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

選択操作              ControlCore                     TermControl
(キーボード/マウス)  -->  _updateSelectionUI()          -->  SelectionStartMarker 表示/非表示
                         TriggerSelection()                 SelectionEndMarker 表示/非表示
                         SelectionMode判定                  マーカー位置・フリップ設定
                         UpdateSelectionMarkers.raise()
                                                     -->  SelectionInfo
                                                          (StartPos, EndPos,
                                                           StartAtLeftBoundary,
                                                           EndAtRightBoundary)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| EventArgs.idl | `src/cascadia/TerminalControl/EventArgs.idl` | IDL | UpdateSelectionMarkersEventArgs定義 |
| EventArgs.h | `src/cascadia/TerminalControl/EventArgs.h` | ソース | UpdateSelectionMarkersEventArgs実装 |
| ControlCore.idl | `src/cascadia/TerminalControl/ControlCore.idl` | IDL | UpdateSelectionMarkersイベント宣言、SelectionData定義 |
| ControlCore.h | `src/cascadia/TerminalControl/ControlCore.h` | ソース | UpdateSelectionMarkersイベント定義、_updateSelectionUI宣言 |
| ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | ソース | _updateSelectionUI実装と全呼び出し箇所 |
| TermControl.cpp | `src/cascadia/TerminalControl/TermControl.cpp` | ソース | _updateSelectionMarkersハンドラ |
| TermControl.h | `src/cascadia/TerminalControl/TermControl.h` | ソース | ハンドラ宣言 |
