# 通知設計書 81-NOTIFICATION_DRAG_END

## 概要

本ドキュメントは、Godotエンジンの`NOTIFICATION_DRAG_END`通知（通知値: 22）について、その送信仕様、処理フロー、実装詳細を記述した設計書である。

### 本通知の処理概要

`NOTIFICATION_DRAG_END`は、GUI上でのドラッグ＆ドロップ操作が終了した際に、シーンツリー内のすべてのノードに伝播される通知である。この通知により、各ノードはドラッグ操作の終了を検知し、必要なクリーンアップ処理や状態リセットを行うことができる。

**業務上の目的・背景**：ドラッグ＆ドロップは、エディタ操作やゲームUIにおける直感的なユーザー操作を実現するための重要な機能である。ドラッグ終了時に適切な状態管理を行わないと、視覚的なフィードバックが残り続けたり、内部状態が不整合になるリスクがある。この通知は、ドラッグ終了後のリソース解放、UIの視覚状態のリセット、ドラッグ成否に応じた後処理（例：選択テキストの削除）を実現するために必要である。

**通知の送信タイミング**：ドラッグ操作が終了する以下のタイミングで送信される。
1. ドロップ可能な領域（`can_drop_data`がtrueを返すControl）上でマウスボタンが離された時（成功ドロップ）
2. ドロップ不可能な領域でマウスボタンが離された時（失敗ドロップ）
3. `gui_cancel_drag()`が呼び出されてドラッグがキャンセルされた時
4. ドラッグ中にエスケープキー等でキャンセルされた時

**通知の受信者**：シーンツリーのセクションルートビューポートを起点として、その配下のすべてのノード（SubViewportContainer経由でないSubViewportを除く）に再帰的に伝播される。Control派生クラス、Node2D、Node3D、エディタプラグインなど、ドラッグ操作の影響を受けるすべてのノードが受信対象となる。

**通知内容の概要**：通知自体にはペイロードは含まれない。ドラッグ操作の成否は`Viewport.gui_is_drag_successful()`メソッド、またはControl派生クラスでは`is_drag_successful()`メソッドを呼び出して確認する。ドラッグデータは`Viewport.gui_get_drag_data()`で取得可能だが、NOTIFICATION_DRAG_END受信時点ではすでにクリアされているため、必要な場合は事前に保存しておく必要がある。

**期待されるアクション**：受信者は以下のような処理を実装することが期待される。
- ドラッグ中の視覚的フィードバック（ハイライト、ドロップインジケータ等）をリセットする
- ドラッグ状態を追跡する内部フラグをfalseに戻す
- ドラッグ成功時、ソース側では元データの削除（例：テキスト選択の削除）
- スクロール処理やタイマー等のドラッグ中限定の処理を停止する
- 必要に応じて`queue_redraw()`を呼び出して再描画をトリガーする

## 通知種別

アプリ内通知（Godot内部通知システム）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高（即座に伝播） |
| リトライ | 無（単発通知） |

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

`Viewport::_propagate_drag_notification()`メソッドにより、以下のロジックで送信先が決定される：

1. セクションルートビューポート（`get_section_root_viewport()`）を起点とする
2. 起点ノードに対して`notification(NOTIFICATION_DRAG_END)`を呼び出す
3. 子ノードを再帰的に走査し、各ノードに通知を伝播する
4. ただし、`SubViewportContainer`の直接の子ではない`SubViewport`はスキップする（独立したビューポートはドラッグコンテキストを共有しないため）

## 通知テンプレート

### 通知パラメータ

| 項目 | 内容 |
|-----|------|
| 通知値 | 22 |
| 通知名 | NOTIFICATION_DRAG_END |
| ペイロード | なし |

### 通知受信パターン

```cpp
void MyControl::_notification(int p_what) {
    switch (p_what) {
        case NOTIFICATION_DRAG_END: {
            // ドラッグ終了時の処理
            if (is_drag_successful()) {
                // ドロップ成功時の処理
            } else {
                // ドロップ失敗/キャンセル時の処理
            }
            // 状態リセット
            drag_active = false;
            queue_redraw();
        } break;
    }
}
```

### GDScript受信パターン

```gdscript
func _notification(what):
    if what == NOTIFICATION_DRAG_END:
        if is_drag_successful():
            # ドロップ成功時の処理
            pass
        else:
            # ドロップ失敗時の処理
            pass
        # 状態リセット
        queue_redraw()
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| p_what | 通知種別（値: 22） | システム定義 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ユーザー操作 | マウスボタンリリース | ドラッグ中（`gui.dragging == true`）の状態でマウスボタンが離された | ドロップ試行時に発火 |
| API呼び出し | `gui_cancel_drag()` | ドラッグ中の状態 | プログラムからのドラッグキャンセル |
| API呼び出し | `gui_perform_drop_at()` | ドラッグ中の状態 | プログラムからのドロップ実行 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ドラッグ未開始 | `gui.dragging`がfalseの場合、NOTIFICATION_DRAG_ENDは送信されない |
| SubViewport除外 | SubViewportContainer経由でないSubViewportには伝播されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ドラッグ終了トリガー発生] --> B{ドラッグ中か?}
    B -->|No| C[何もしない]
    B -->|Yes| D[gui_perform_drop_at呼び出し]
    D --> E{ドロップ先あり?}
    E -->|Yes| F[_gui_drop実行]
    E -->|No| G[drag_successful = false]
    F --> H[drag_successful設定]
    H --> I[ドラッグプレビュー削除]
    G --> I
    I --> J[gui.drag_data = Variant]
    J --> K[gui.dragging = false]
    K --> L[gui.global_dragging = false]
    L --> M[_propagate_drag_notification]
    M --> N[NOTIFICATION_DRAG_END伝播]
    N --> O[各ノードで_notification受信]
    O --> P[update_mouse_cursor_state]
    P --> Q[終了]
```

### 通知伝播フロー

```mermaid
flowchart TD
    A[セクションルートViewport] --> B[notification DRAG_END]
    B --> C{SubViewportContainerか?}
    C -->|Yes| D[子をすべて走査]
    C -->|No| E{SubViewportか?}
    E -->|Yes| F[スキップ]
    E -->|No| D
    D --> G[各子ノードに再帰伝播]
    G --> H[全ノード処理完了]
```

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

該当なし（データベースを使用しない内部通知システム）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 不正なドラッグ状態 | ドラッグ未開始状態でのドロップ試行 | `ERR_FAIL_COND`でガード、処理スキップ |
| 無効なドラッグデータ | `force_drag`で`NIL`データを設定しようとした場合 | エラーメッセージ出力、処理中断 |
| スレッド違反 | メインスレッド以外からの呼び出し | `ERR_MAIN_THREAD_GUARD`でガード |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（単発通知） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし（ユーザー操作依存） |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし（ユーザー操作に即座に反応）

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

- ドラッグデータは`Variant`型で任意のデータを含むことができるため、`can_drop_data`での検証が重要
- ドラッグ操作はViewportのセクションルートを共有するノード間でのみ有効であり、異なるウィンドウ間では独立している
- `_get_drag_data`で機密情報を含めないよう注意が必要

## 備考

- `NOTIFICATION_DRAG_BEGIN`（値: 21）と対になる通知
- ドラッグの成否確認は`Viewport.gui_is_drag_successful()`または`Control.is_drag_successful()`を使用
- ポーリングでドラッグ状態を監視する場合は`Viewport.gui_is_dragging()`が代替手段となる
- エディタでは、ファイルシステムドック、シーンツリードック、インスペクタ等多くのコンポーネントがこの通知を活用している

---

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

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

### 推奨読解順序

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

まず、通知定数の定義とドラッグ状態を管理する構造体を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | node.h | `scene/main/node.h` | 463行目: `NOTIFICATION_DRAG_END = 22`の定義を確認 |
| 1-2 | viewport.h | `scene/main/viewport.h` | GUI構造体内のドラッグ関連メンバ変数を確認 |

**読解のコツ**: Node.hの通知定数はenumとして定義されており、NOTIFICATION_DRAG_BEGIN(21)とNOTIFICATION_DRAG_END(22)が連番で定義されている。

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

ドラッグ終了処理の起点となる関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | viewport.cpp | `scene/main/viewport.cpp` | `gui_perform_drop_at()`関数がドラッグ終了のエントリーポイント |

**主要処理フロー**:
1. **2412-2418行**: ドロップ成否の判定（`_gui_drop`呼び出しまたは`false`設定）
2. **2420-2424行**: ドラッグプレビューの削除処理
3. **2426-2431行**: ドラッグ状態のクリア処理
4. **2432行**: `_propagate_drag_notification(section_root, NOTIFICATION_DRAG_END)`呼び出し
5. **2434行**: カーソル状態の更新

#### Step 3: 通知伝播メカニズムを理解する

通知がどのようにシーンツリー全体に伝播されるかを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | viewport.cpp | `scene/main/viewport.cpp` | `_propagate_drag_notification()`の実装 |

**主要処理フロー**:
- **1333-1344行**: 再帰的な通知伝播処理
- **1335行**: 対象ノードへの`notification(p_what)`呼び出し
- **1336-1340行**: SubViewportContainerとSubViewportの特殊処理
- **1342行**: 子ノードへの再帰呼び出し

#### Step 4: 受信側実装パターンを理解する

実際の受信処理の実装パターンを複数のファイルで確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | line_edit.cpp | `scene/gui/line_edit.cpp` | テキスト編集での受信処理パターン |
| 4-2 | text_edit.cpp | `scene/gui/text_edit.cpp` | マルチラインテキスト編集での受信処理 |
| 4-3 | tree.cpp | `scene/gui/tree.cpp` | ツリーコントロールでの受信処理 |
| 4-4 | scroll_container.cpp | `scene/gui/scroll_container.cpp` | スクロールコンテナでの受信処理 |

**主要処理フロー（line_edit.cpp: 1707-1723行）**:
- **1708行**: `is_drag_successful()`でドラッグ成否を確認
- **1709-1716行**: 成功時、選択テキストの削除または選択解除
- **1717-1719行**: 失敗時、drag_attemptフラグのリセット
- **1720-1722行**: 共通クリーンアップ処理

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

```
Viewport::gui_perform_drop_at()          [scene/main/viewport.cpp:2412]
    │
    ├─ Viewport::_gui_drop()             [ドロップ処理実行]
    │
    ├─ memdelete(drag_preview)           [プレビュー削除]
    │
    ├─ get_section_root_viewport()       [セクションルート取得]
    │
    └─ Viewport::_propagate_drag_notification()  [scene/main/viewport.cpp:1333]
           │
           ├─ Node::notification(NOTIFICATION_DRAG_END)
           │      │
           │      └─ [各派生クラスの_notification()]
           │             ├─ LineEdit::_notification()      [scene/gui/line_edit.cpp:1707]
           │             ├─ TextEdit::_notification()      [scene/gui/text_edit.cpp:2012]
           │             ├─ Tree::_notification()          [scene/gui/tree.cpp:5019]
           │             ├─ TabBar::_notification()        [scene/gui/tab_bar.cpp:497]
           │             ├─ ScrollContainer::_notification() [scene/gui/scroll_container.cpp:484]
           │             └─ [その他のControl派生クラス...]
           │
           └─ [再帰: 子ノードへ伝播]
```

### データフロー図

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

マウスリリース          Viewport                            各ノードの
イベント     ───────▶  ::gui_perform_drop_at() ───────▶    _notification()
                              │                                  │
                              ▼                                  ▼
                        ドラッグ状態                        状態リセット
                        のクリア                            UIの再描画
                              │                             成功時の後処理
                              ▼
                        _propagate_drag_notification()
                              │
                              ▼
                        NOTIFICATION_DRAG_END
                        をシーンツリーに伝播
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| node.h | `scene/main/node.h` | ソース | NOTIFICATION_DRAG_END定数の定義（463行目） |
| node.cpp | `scene/main/node.cpp` | ソース | 定数のバインディング（3952行目） |
| viewport.h | `scene/main/viewport.h` | ソース | GUI状態管理構造体、_propagate_drag_notification宣言 |
| viewport.cpp | `scene/main/viewport.cpp` | ソース | 通知発火・伝播ロジックの実装 |
| control.cpp | `scene/gui/control.cpp` | ソース | is_drag_successful()の実装（2252行目） |
| line_edit.cpp | `scene/gui/line_edit.cpp` | ソース | 受信処理実装例（1707-1723行目） |
| text_edit.cpp | `scene/gui/text_edit.cpp` | ソース | 受信処理実装例（2012-2026行目） |
| tree.cpp | `scene/gui/tree.cpp` | ソース | 受信処理実装例（5019-5024行目） |
| tab_bar.cpp | `scene/gui/tab_bar.cpp` | ソース | 受信処理実装例（497-509行目） |
| scroll_container.cpp | `scene/gui/scroll_container.cpp` | ソース | 受信処理実装例（484-486行目） |
| rich_text_label.cpp | `scene/gui/rich_text_label.cpp` | ソース | 受信処理実装例（2704行目） |
| filesystem_dock.cpp | `editor/docks/filesystem_dock.cpp` | ソース | エディタでの受信処理例（611-618行目） |
| scene_tree_dock.cpp | `editor/docks/scene_tree_dock.cpp` | ソース | エディタでの受信処理例（1800行目） |
| editor_inspector.cpp | `editor/inspector/editor_inspector.cpp` | ソース | エディタでの受信処理例（2208行目、3328行目） |
| Node.xml | `doc/classes/Node.xml` | ドキュメント | API仕様記述（1205-1208行目） |
| Viewport.xml | `doc/classes/Viewport.xml` | ドキュメント | 関連APIの仕様記述 |
| Control.xml | `doc/classes/Control.xml` | ドキュメント | is_drag_successful()の仕様記述（748-753行目） |
| test_viewport.h | `tests/scene/test_viewport.h` | テスト | ユニットテスト実装（133-136行目） |
