# 機能設計書 154-BeginMenu

## 概要

本ドキュメントはImGuiのBeginMenu機能について、その処理内容、入出力仕様、実装詳細を記述した機能設計書である。

### 本機能の処理概要

BeginMenuは、メニューバーまたは既存のメニュー内にサブメニューエントリを作成するための関数である。クリックまたはホバーによってサブメニューポップアップを開き、階層的なメニュー構造を構築できる。

**業務上の目的・背景**：アプリケーションのメニューシステムでは、「ファイル」>「最近使ったファイル」のような階層構造が一般的である。BeginMenuは、このような入れ子のメニュー構造を簡単に実装するための機能を提供する。メニューバー内での水平配置と、サブメニュー内での垂直配置の両方に対応し、マウスホバーやキーボードナビゲーションによる自然なメニュー操作を実現する。

**機能の利用シーン**：メニューバー内でのトップレベルメニュー項目（「ファイル」「編集」など）の作成、既存メニュー内でのサブメニュー（「エクスポート」>「PNG」「JPG」など）の作成に使用される。Main Menu Bar画面やDemo Windowなどで使用される。

**主要な処理内容**：
1. ラベルからIDを計算し、ポップアップの開閉状態を確認する
2. メニューバー（水平）またはサブメニュー（垂直）のレイアウトに応じた描画を行う
3. マウスホバー、クリック、キーボードナビゲーションによる開閉制御を行う
4. サブメニューへのマウス移動を検出し、Amazonメガドロップダウン方式で直感的なナビゲーションを実現する
5. ポップアップウィンドウを作成してサブメニューコンテンツを表示する

**関連システム・外部連携**：ImGuiのポップアップシステム、ナビゲーションシステムと連携。SelectableやRenderText等の描画関数を使用。

**権限による制御**：enabledパラメータによりメニューの有効/無効を制御可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | Main Menu Bar | 主機能 | サブメニューの開始 |

## 機能種別

ウィジェット / ポップアップ制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| label | const char* | Yes | メニューのラベルテキスト | NULL終端文字列 |
| enabled | bool | No | メニューが有効かどうか（デフォルト: true） | - |

### 入力データソース

- ImGuiContext（グローバルコンテキスト）
- ImGuiWindow（現在のウィンドウ）
- ImGuiStyle（スタイル設定）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | bool | メニューが開いている場合true。trueの場合のみEndMenu()を呼び出す |

### 出力先

- ポップアップウィンドウ（メニューが開いている場合）
- 描画リスト（メニュー項目の描画）

## 処理フロー

### 処理シーケンス

```
1. 初期化とID計算
   └─ ウィンドウ取得とSkipItemsチェック
   └─ ラベルからIDを計算
   └─ ポップアップの開閉状態を確認

2. 同一IDのメニューが既に存在する場合の処理
   └─ 追加モードとして既存ポップアップに追加

3. レイアウトに応じた描画
   └─ 水平レイアウト（メニューバー内）: 水平方向に配置
   └─ 垂直レイアウト（サブメニュー内）: 垂直方向に配置、矢印を描画

4. ホバー・クリック・ナビゲーション処理
   └─ クリックで開閉
   └─ ホバーで開く（他のメニューが開いている場合）
   └─ キーボード/ゲームパッドのナビゲーション

5. サブメニューへの移動検出（Amazonメガドロップダウン方式）
   └─ マウスが子メニューに向かっているか三角形判定

6. ポップアップの開閉
   └─ want_openがtrueならOpenPopup()
   └─ want_closeがtrueならClosePopupToLevel()

7. ポップアップウィンドウの作成
   └─ SetNextWindowPosで位置を設定
   └─ BeginPopupMenuExでポップアップを開始
```

### フローチャート

```mermaid
flowchart TD
    A[開始: BeginMenu] --> B{SkipItems?}
    B -->|Yes| C[return false]
    B -->|No| D[ID計算、menu_is_open取得]
    D --> E{同一IDのメニューが<br/>既に存在?}
    E -->|Yes| F[既存ポップアップに追加]
    E -->|No| G[メニューIDを登録]
    G --> H{水平レイアウト?}
    H -->|Yes| I[水平メニュー描画]
    H -->|No| J[垂直メニュー描画<br/>矢印付き]
    I --> K[ホバー・クリック判定]
    J --> K
    K --> L{開く必要あり?}
    L -->|Yes| M[OpenPopup]
    L -->|No| N{閉じる必要あり?}
    M --> O{menu_is_open?}
    N -->|Yes| P[ClosePopupToLevel]
    N -->|No| O
    P --> O
    O -->|Yes| Q[SetNextWindowPos<br/>BeginPopupMenuEx]
    O -->|No| R[NextWindowData.ClearFlags]
    Q --> S[return menu_is_open]
    R --> S
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-154-01 | ペア呼び出し | 戻り値がtrueの場合のみEndMenu()を呼び出す | 常時 |
| BR-154-02 | ホバー開閉 | 他のメニューが開いている状態でホバーすると自動的に開く | menuset_is_open |
| BR-154-03 | クリック開閉 | 水平メニューバーでは開いているメニューをクリックで閉じられる | 水平レイアウト時 |
| BR-154-04 | 無効時自動閉じ | enabledがfalseになると開いているメニューは自動的に閉じる | 常時 |
| BR-154-05 | 三角形判定 | マウスが子メニュー方向に移動中はメニューを閉じない | 垂直レイアウト時 |

### 計算ロジック

- 三角形判定（Amazonメガドロップダウン方式）:
  - マウス位置、子メニューの左上/左下（または右上/右下）の3点で三角形を形成
  - マウスが三角形内にある場合は子メニュー方向に移動中と判定

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

データベース操作なし（UI描画のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 早期リターン | window->SkipItems == true | falseを返す |
| - | クリップ | ポップアップが完全にクリップされている | falseを返す |

### リトライ仕様

リトライ不要（フレームごとに再実行）

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

トランザクション管理なし（即時描画）

## パフォーマンス要件

- 同一IDのBeginMenuはO(N)検索（Nはフレーム内のメニュー数）
- 通常のメニュー数であれば問題なし

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

- 特になし

## 備考

- BeginMenuEx()という拡張版があり、アイコン表示にも対応
- サブメニューはChildWindowフラグを持ち、マウスホバーが親子間で機能する
- 同一フレーム内で同じIDのBeginMenuを複数回呼び出すと追加モードになる

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | imgui.h | `imgui.h` | 813行目: BeginMenu API宣言を確認 |
| 1-2 | imgui_internal.h | `imgui_internal.h` | ImGuiPopupData、ImGuiMenuColumnsの定義を確認 |

**読解のコツ**: BeginMenuは内部でBeginMenuExを呼び出す薄いラッパー。実際のロジックはBeginMenuExにある。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | imgui_widgets.cpp | `imgui_widgets.cpp` | 9403-9406行目: BeginMenu関数（BeginMenuExへのラッパー） |
| 2-2 | imgui_widgets.cpp | `imgui_widgets.cpp` | 9190-9401行目: BeginMenuEx関数の実装 |

**主要処理フロー**:
1. **9192-9199行目**: ウィンドウ取得、ID計算、開閉状態確認
2. **9201-9217行目**: 同一IDのメニュー処理
3. **9224-9228行目**: menuset_is_openの判定
4. **9243-9276行目**: レイアウトに応じた描画
5. **9284-9354行目**: ホバー、クリック、ナビゲーション処理
6. **9360-9400行目**: ポップアップの開閉とウィンドウ作成

#### Step 3: 三角形判定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | imgui_widgets.cpp | `imgui_widgets.cpp` | 9288-9310行目: Amazonメガドロップダウン方式の実装 |

**主要処理フロー**:
- **9296-9301行目**: 三角形の3頂点を計算
- **9308行目**: ImTriangleContainsPointで判定

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

```
ImGui::BeginMenu(label, enabled)
    │
    └─ BeginMenuEx(label, NULL, enabled)
           │
           ├─ GetCurrentWindow()
           ├─ window->GetID(label)
           ├─ IsPopupOpen(id)
           │
           ├─ [同一ID処理]
           │      └─ BeginPopupMenuEx() [追加モード]
           │
           ├─ CalcTextSize(label)
           │
           ├─ IsRootOfOpenMenuSet()
           │      └─ メニューセットのルートか判定
           │
           ├─ PushID(label)
           ├─ BeginDisabled() [enabledがfalseの場合]
           │
           ├─ Selectable() + RenderText() / RenderArrow()
           │      └─ メニュー項目を描画
           │
           ├─ EndDisabled() [enabledがfalseの場合]
           ├─ PopID()
           │
           ├─ [三角形判定]
           │      └─ ImTriangleContainsPoint()
           │
           ├─ OpenPopup() [開く場合]
           ├─ ClosePopupToLevel() [閉じる場合]
           │
           └─ BeginPopupMenuEx() [開いている場合]
                  └─ ポップアップウィンドウを作成
```

### データフロー図

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

label ────────────────────> GetID ───────────────────────> id
                                │
IsPopupOpen(id) ──────────────> 開閉状態確認 ─────────────> menu_is_open
                                │
g.IO.MousePos ────────────────> 三角形判定 ─────────────────> moving_toward_child_menu
                                │
hovered/pressed ──────────────> 開閉判定 ───────────────────> want_open/want_close
                                │
                                ├──────────────────────────> OpenPopup() or ClosePopupToLevel()
                                │
                                └──────────────────────────> BeginPopupMenuEx() ──> ポップアップウィンドウ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| imgui.h | `imgui.h` | ヘッダー | BeginMenu APIの宣言 |
| imgui_internal.h | `imgui_internal.h` | ヘッダー | ImGuiPopupData、IsRootOfOpenMenuSet等の内部定義 |
| imgui_widgets.cpp | `imgui_widgets.cpp` | ソース | BeginMenu実装（9403-9406行目）、BeginMenuEx実装（9190-9401行目） |
| imgui.cpp | `imgui.cpp` | ソース | OpenPopup、ClosePopupToLevel等のポップアップ関数 |
