# 機能設計書 147-ListBox

## 概要

本ドキュメントは、Dear ImGuiライブラリにおけるListBox関数の機能設計を記述する。ListBoxはBeginListBox/EndListBoxのラッパーとして、シンプルなリスト選択UIを提供するヘルパー関数である。

### 本機能の処理概要

ListBox関数は、文字列配列またはコールバック関数から取得したアイテムのリストを表示し、ユーザーが1つのアイテムを選択できるリストボックスを作成する。内部的にはBeginListBox/EndListBoxとSelectableを組み合わせて実装されている。

**業務上の目的・背景**：多くのアプリケーションでは、単純な文字列リストから1つを選択するUIが必要になる。ListBoxは、このような一般的なユースケースに対して、最小限のコードで実装できるヘルパー関数を提供する。

**機能の利用シーン**：
- 設定画面でのオプション選択
- フォントセレクタ
- スタイルセレクタ
- 言語選択
- 簡易的なファイル選択

**主要な処理内容**：
1. 高さの計算（アイテム数ベース）
2. BeginListBoxの呼び出し
3. ImGuiListClipperによる効率的なアイテム描画
4. Selectableを使用した各アイテムの表示
5. 選択状態の更新
6. EndListBoxの呼び出し

**関連システム・外部連携**：
- BeginListBox/EndListBoxを内部で使用
- Selectableを使用してアイテムを表示
- ImGuiListClipperで仮想スクロールを実現

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 23 | Widgets | 補助機能 | リストボックスのデモ |

## 機能種別

ヘルパー関数 / ウィジェット作成 / 選択UI

## 入力仕様

### 入力パラメータ（配列版）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| label | const char* | Yes | リストボックスのラベル | NULL不可 |
| current_item | int* | Yes | 現在選択されているアイテムのインデックス | 有効なポインタ |
| items | const char* const[] | Yes | アイテム文字列の配列 | NULL不可 |
| items_count | int | Yes | アイテム数 | >= 0 |
| height_in_items | int | No | 表示するアイテム数（-1でデフォルト） | >= -1 |

### 入力パラメータ（コールバック版）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| label | const char* | Yes | リストボックスのラベル | NULL不可 |
| current_item | int* | Yes | 現在選択されているアイテムのインデックス | 有効なポインタ |
| getter | const char* (*)(void*, int) | Yes | アイテム文字列を取得するコールバック | NULL不可 |
| user_data | void* | Yes | コールバックに渡すユーザーデータ | 任意 |
| items_count | int | Yes | アイテム数 | >= 0 |
| height_in_items | int | No | 表示するアイテム数（-1でデフォルト） | >= -1 |

### 入力データソース

- 呼び出し元からの直接パラメータ指定
- 配列またはコールバックからのアイテムデータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 戻り値 | bool | 選択が変更された場合true |
| current_item | int* | 新しく選択されたアイテムのインデックス |

### 出力先

- current_itemへの選択インデックス更新
- 画面描画（リストボックスUI）

## 処理フロー

### 処理シーケンス

```
1. 高さの計算
   └─ height_in_items < 0 の場合、min(items_count, 7) を使用
   └─ 端数（0.25f）を追加してスクロール可能を示唆

2. サイズの決定
   └─ GetTextLineHeightWithSpacing() * height_in_items_f + FramePadding.y * 2

3. BeginListBoxの呼び出し
   └─ falseの場合は早期リターン

4. クリッパーの設定
   └─ ImGuiListClipper で効率的な描画を準備
   └─ current_itemをIncludeItemByIndexで含める

5. アイテムのループ
   └─ clipper.Stepでクリップされた範囲を取得
   └─ 各アイテムでSelectable呼び出し
   └─ 選択されたらcurrent_itemを更新

6. フォーカス設定
   └─ 選択されたアイテムにSetItemDefaultFocus

7. EndListBoxの呼び出し

8. 変更のマーク
   └─ value_changedの場合MarkItemEdited
```

### フローチャート

```mermaid
flowchart TD
    A[ListBox開始] --> B[高さ計算]
    B --> C[サイズ決定]
    C --> D[BeginListBox]
    D --> E{成功?}
    E -->|No| F[return false]
    E -->|Yes| G[ImGuiListClipper設定]
    G --> H[IncludeItemByIndex current_item]
    H --> I{clipper.Step}
    I -->|Yes| J[DisplayStart〜DisplayEndループ]
    J --> K[アイテム文字列取得]
    K --> L[PushID]
    L --> M[Selectable]
    M --> N{選択された?}
    N -->|Yes| O[current_item更新]
    N -->|No| P{item_selected?}
    O --> P
    P -->|Yes| Q[SetItemDefaultFocus]
    P -->|No| R[PopID]
    Q --> R
    R --> I
    I -->|No| S[EndListBox]
    S --> T{value_changed?}
    T -->|Yes| U[MarkItemEdited]
    T -->|No| V[return value_changed]
    U --> V
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-147-01 | デフォルト高さ | height_in_items = -1でmin(items_count, 7)を使用 | height_in_items < 0 |
| BR-147-02 | 端数追加 | 0.25fを追加してスクロール可能を示唆 | 常時 |
| BR-147-03 | クリッパー使用 | ImGuiListClipperで効率的な描画 | 常時 |
| BR-147-04 | フォーカス設定 | 選択アイテムにデフォルトフォーカス | 常時 |

### 計算ロジック

高さ計算:
```cpp
if (height_in_items < 0)
    height_in_items = ImMin(items_count, 7);
float height_in_items_f = height_in_items + 0.25f;
ImVec2 size(0.0f, ImTrunc(GetTextLineHeightWithSpacing() * height_in_items_f + g.Style.FramePadding.y * 2.0f));
```

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

該当なし（ImGuiはインメモリUIライブラリ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 表示エラー | getterがNULLを返した場合 | "*Unknown item*"を表示 |

### リトライ仕様

該当なし

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

選択変更は即座にcurrent_itemに反映され、trueを返す。

## パフォーマンス要件

- ImGuiListClipperにより、表示範囲外のアイテムはスキップ
- 大量のアイテムでも効率的に動作

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

getterコールバックが有効なメモリを返すことを保証すること。

## 備考

- カスタム選択ロジックが必要な場合はBeginListBox/EndListBoxを直接使用
- 高さをアイテム数から計算する場合、10.5fのような端数を使用してスクロール可能を示唆可能
- コールバック版は動的なデータソースに対応

---

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

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

### 推奨読解順序

#### Step 1: API宣言を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | imgui.h | `imgui.h` | ListBox関数宣言（787-788行目）で2つのオーバーロードを確認 |

**読解のコツ**: 配列版とコールバック版の2つのオーバーロードがある。

#### Step 2: 実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | imgui_widgets.cpp | `imgui_widgets.cpp` | 配列版ListBox実装（8682-8686行目） |
| 2-2 | imgui_widgets.cpp | `imgui_widgets.cpp` | コールバック版ListBox実装（8690-8733行目） |

**主要処理フロー**:
1. **8695-8698行目**: 高さ計算
2. **8700-8701行目**: BeginListBox呼び出しと早期リターン
3. **8706-8708行目**: ImGuiListClipper設定
4. **8709-8726行目**: アイテムループとSelectable
5. **8727行目**: EndListBox呼び出し
6. **8729-8730行目**: MarkItemEdited

#### Step 3: 配列ゲッターを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | imgui_widgets.cpp | `imgui_widgets.cpp` | Items_ArrayGetter関数（配列版で使用） |

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

```
ListBox(label, current_item, items[], items_count, height_in_items)
    │
    └─ ListBox(label, current_item, getter, user_data, items_count, height_in_items)
           │
           ├─ BeginListBox(label, size)
           │
           ├─ ImGuiListClipper.Begin(items_count, GetTextLineHeightWithSpacing())
           │     └─ IncludeItemByIndex(*current_item)
           │
           ├─ [loop] clipper.Step()
           │     │
           │     ├─ getter(user_data, i) → item_text
           │     │
           │     ├─ PushID(i)
           │     │
           │     ├─ Selectable(item_text, item_selected)
           │     │     └─ [if selected] *current_item = i
           │     │
           │     ├─ [if item_selected] SetItemDefaultFocus()
           │     │
           │     └─ PopID()
           │
           ├─ EndListBox()
           │
           └─ [if value_changed] MarkItemEdited(g.LastItemData.ID)
```

### データフロー図

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

label ─────────────────────┐
                           │
current_item ──────────────┼──▶ ListBox() ────────────────▶ bool (value_changed)
                           │         │
items[] / getter ──────────┤         │
                           │         ├──▶ *current_item更新
items_count ───────────────┤         │
                           │         └──▶ リストボックス描画
height_in_items ───────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| imgui.h | `imgui.h` | ヘッダー | 公開API宣言 |
| imgui_widgets.cpp | `imgui_widgets.cpp` | ソース | ListBox実装 |
