# 機能設計書 50-FocusManager

## 概要

本ドキュメントは、Avalonia UIフレームワークにおけるFocusManager（フォーカス管理）機能の設計仕様を定義する。FocusManagerはアプリケーション全体のキーボードフォーカスを管理する中心的なクラスである。

### 本機能の処理概要

FocusManagerクラスは、キーボードフォーカスの取得・喪失・移動を管理するクラスである。現在のフォーカス要素の追跡、フォーカススコープの管理、Tab/Shift+Tabによるフォーカス移動、XYフォーカス（方向キーによるフォーカス移動）などを提供する。

**業務上の目的・背景**：キーボードによるUIナビゲーションはアクセシビリティの観点から必須である。FocusManagerはフォーカスの一元管理を行い、アプリケーション全体で一貫したフォーカス動作を実現する。

**機能の利用シーン**：Tab/Shift+Tabによるフォーカス移動、方向キーによるフォーカス移動（ゲームパッド対応）、プログラムによるフォーカス制御、フォーカススコープ間の遷移など。

**主要な処理内容**：
1. 現在のフォーカス要素の追跡
2. フォーカスの設定・解除
3. フォーカススコープの管理
4. タブナビゲーション（次/前のタブストップ検索）
5. XYフォーカス（方向ベースのフォーカス移動）
6. フォーカス可能な最初/最後の要素の検索

**関連システム・外部連携**：InputElement、KeyboardDevice、TopLevel、IFocusScopeと連携する。

**権限による制御**：本機能に権限による制御はない。すべてのアプリケーションで使用可能である。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| すべて | すべての画面 | 利用 | すべてのフォーカス操作で使用 |

## 機能種別

フォーカス管理 / ナビゲーション / アクセシビリティ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| control | IInputElement | No（Focus用） | フォーカスを設定する要素 | CanFocusチェック |
| method | NavigationMethod | No | フォーカス取得方法 | Unspecified/Tab/Pointer/Directional |
| keyModifiers | KeyModifiers | No | キー修飾子 | - |
| direction | NavigationDirection | Yes（移動用） | 移動方向 | Next/Previous/Up/Down/Left/Right |

### 入力データソース

- InputElement.Focus(): プログラムからのフォーカス要求
- KeyboardDevice: Tabキー、方向キーによるフォーカス移動
- PointerEvent: クリックによるフォーカス取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| GetFocusedElement() | IInputElement? | 現在フォーカスを持つ要素 |
| FindFirstFocusableElement() | IInputElement? | 最初のフォーカス可能要素 |
| FindLastFocusableElement() | IInputElement? | 最後のフォーカス可能要素 |
| FindNextElement() | IInputElement? | 次のフォーカス要素 |

### 出力先

- KeyboardDevice: フォーカス要素の設定
- InputElement: GotFocus/LostFocusイベント発火

## 処理フロー

### 処理シーケンス

```
1. Focus(control) 呼び出し
   └─ CanFocus チェック
   └─ フォーカススコープ取得
   └─ KeyboardDevice.SetFocusedElement

2. TryMoveFocus(direction) 呼び出し
   └─ FindNextElement で次要素検索
   └─ Focus で設定

3. ClearFocus 呼び出し
   └─ Focus(null) で解除

4. フォーカススコープ変更
   └─ SetFocusScope で前回要素を復元
```

### フローチャート

```mermaid
flowchart TD
    A[Focus要求] --> B{CanFocus?}
    B -->|No| C[false返却]
    B -->|Yes| D[フォーカススコープ取得]
    D --> E[FocusedElement保存]
    E --> F[KeyboardDevice.SetFocusedElement]
    F --> G[GotFocusイベント発火]
    G --> H[true返却]

    I[TryMoveFocus] --> J[FindNextElement]
    J --> K{要素あり?}
    K -->|Yes| L[Focus]
    K -->|No| M[false返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | フォーカス条件 | Focusable && IsEffectivelyEnabled && IsVisible && IsAttached | Focus時 |
| BR-002 | スコープ記憶 | 各フォーカススコープで最後のフォーカス要素を記憶 | スコープ退出時 |
| BR-003 | タブサイクル | KeyboardNavigationMode.Cycleでスコープ内循環 | Tab時 |
| BR-004 | タブOnce | KeyboardNavigationMode.Onceでスコープ要素のみ | Tab時 |

### 計算ロジック

**CanFocus判定**：
```
CanFocus = Focusable && IsEffectivelyEnabled && IsVisible(v.IsAttachedToVisualTree && v.IsEffectivelyVisible)
```

**タブインデックス比較**：
```
CompareTabIndex = control1.TabIndex.CompareTo(control2.TabIndex)
```

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | - | フォーカス操作でのエラーは発生しない設計 |

### リトライ仕様

リトライは適用されない。フォーカス失敗時はfalseを返す。

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

トランザクション管理は不要。

## パフォーマンス要件

- Focus操作は即座に完了
- FindNextElement検索は効率的なツリー走査
- 大規模UIでもタブナビゲーションはスムーズ

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

特になし。

## 備考

- GetFocusManager(IInputElement)でTopLevelからFocusManagerを取得
- XYFocusで方向ベースのフォーカス移動（Xbox、リモコン対応）

---

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

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

### 推奨読解順序

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

FocusManagerクラスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | フィールド | `src/Avalonia.Base/Input/FocusManager.cs` | 25-26行目、28行目、60-64行目。スコープ管理、XYFocus |
| 1-2 | FocusedElementProperty | `src/Avalonia.Base/Input/FocusManager.cs` | 25-26行目。フォーカス要素の添付プロパティ |

**読解のコツ**: _focusRootがフォーカススコープのルート要素を保持。

#### Step 2: 基本操作を理解する

Focus/ClearFocusの処理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | GetFocusedElement | `src/Avalonia.Base/Input/FocusManager.cs` | 69行目。現在フォーカス要素取得 |
| 2-2 | Focus | `src/Avalonia.Base/Input/FocusManager.cs` | 77-111行目。フォーカス設定本体 |
| 2-3 | ClearFocus | `src/Avalonia.Base/Input/FocusManager.cs` | 113-116行目。フォーカス解除 |
| 2-4 | CanFocus | `src/Avalonia.Base/Input/FocusManager.cs` | 205行目。フォーカス可能判定 |

**主要処理フロー**:
- **82-83行目**: KeyboardDeviceのnullチェック
- **85-88行目**: CanFocusチェック
- **90-94行目**: フォーカススコープ取得とFocusedElement保存
- **96行目**: KeyboardDevice.SetFocusedElement呼び出し

#### Step 3: フォーカススコープを理解する

スコープ間のフォーカス管理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | GetFocusScope | `src/Avalonia.Base/Input/FocusManager.cs` | 223-242行目。スコープ検索 |
| 3-2 | GetFocusRoot | `src/Avalonia.Base/Input/FocusManager.cs` | 244-258行目。ルート検索 |
| 3-3 | SetFocusScope | `src/Avalonia.Base/Input/FocusManager.cs` | 141-154行目。スコープ変更時のフォーカス復元 |

**主要処理フロー**:
- **143-146行目**: スコープの以前のフォーカス要素を復元
- **147-153行目**: スコープ自体がフォーカス可能な場合

#### Step 4: タブナビゲーションを理解する

Tab/Shift+Tabによるフォーカス移動。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | TryMoveFocus | `src/Avalonia.Base/Input/FocusManager.cs` | 184-198行目。フォーカス移動試行 |
| 4-2 | FindNextFocus | `src/Avalonia.Base/Input/FocusManager.cs` | 390-417行目。次フォーカス検索 |
| 4-3 | GetNextTabStop | `src/Avalonia.Base/Input/FocusManager.cs` | 612-718行目。次タブストップ検索 |
| 4-4 | GetPreviousTabStop | `src/Avalonia.Base/Input/FocusManager.cs` | 720-838行目。前タブストップ検索 |

**主要処理フロー**:
- **396-400行目**: Previous/NextでProcessTabStopInternal呼び出し
- **403-414行目**: 方向キーでXYFocus検索

#### Step 5: ポインタフォーカスを理解する

クリックによるフォーカス取得。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | OnPreviewPointerEventHandler | `src/Avalonia.Base/Input/FocusManager.cs` | 265-289行目。クリック時フォーカス |

**主要処理フロー**:
- **35-42行目**: 静的コンストラクタでPointerPressedをTunnelで購読
- **273行目**: 左クリック判定
- **277-284行目**: フォーカス可能要素を検索してFocus

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

```
Focus(control)
    │
    ├─ CanFocus(control)
    │      └─ Focusable && IsEffectivelyEnabled && IsVisible
    │
    ├─ GetFocusScope(control)
    │      └─ IFocusScope検索
    │
    ├─ scope.SetValue(FocusedElementProperty, control)
    │
    └─ KeyboardDevice.SetFocusedElement
           └─ GotFocusイベント発火

TryMoveFocus(direction)
    │
    └─ FindAndSetNextFocus
           │
           ├─ FindNextFocus(direction, options)
           │      │
           │      ├─ ProcessTabStopInternal (Tab/Previous)
           │      │      ├─ GetNextTabStop
           │      │      └─ GetPreviousTabStop
           │      │
           │      └─ XYFocus.GetNextFocusableElement (方向)
           │
           └─ Focus(nextElement)

OnPreviewPointerEventHandler (Tunnel)
    │
    ├─ 左クリック判定
    └─ while (element != null)
           ├─ CanPointerFocus(element)
           └─ Focus(element)
```

### データフロー図

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

Focus(control) ─────────▶ CanFocusチェック
                              │
                              ▼
                         GetFocusScope
                              │
                              ▼
                    FocusedElementProperty設定
                              │
                              ▼
                    KeyboardDevice.SetFocusedElement
                              │
                              ▼
                    [GotFocusイベント]

Tab押下 ────────────────▶ TryMoveFocus(Next)
                              │
                              ▼
                         FindNextFocus
                              │
                              ▼
                         GetNextTabStop
                              │
                              ▼
                    [次フォーカス要素へFocus]

方向キー ───────────────▶ TryMoveFocus(Up/Down/Left/Right)
                              │
                              ▼
                    XYFocus.GetNextFocusableElement
                              │
                              ▼
                    [方向の要素へFocus]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| FocusManager.cs | `src/Avalonia.Base/Input/FocusManager.cs` | ソース | FocusManagerクラスの実装 |
| IFocusManager.cs | `src/Avalonia.Base/Input/IFocusManager.cs` | ソース | インターフェース定義 |
| InputElement.cs | `src/Avalonia.Base/Input/InputElement.cs` | ソース | フォーカス対象クラス |
| KeyboardDevice.cs | `src/Avalonia.Base/Input/KeyboardDevice.cs` | ソース | キーボードデバイス |
| KeyboardNavigation.cs | `src/Avalonia.Base/Input/KeyboardNavigation.cs` | ソース | ナビゲーション設定 |
| XYFocus.cs | `src/Avalonia.Base/Input/Navigation/XYFocus.cs` | ソース | 方向ベースフォーカス |
| FocusHelpers.cs | `src/Avalonia.Base/Input/Navigation/FocusHelpers.cs` | ソース | フォーカスユーティリティ |
| IFocusScope.cs | `src/Avalonia.Base/Input/IFocusScope.cs` | ソース | フォーカススコープインターフェース |
