# 機能設計書 40-GridSplitter

## 概要

本ドキュメントは、Avalonia UIフレームワークにおけるGridSplitter（グリッドスプリッター）の機能設計を定義するものである。GridSplitterはGridコントロールの列または行のサイズをユーザー操作で変更するためのコントロールを提供する。

### 本機能の処理概要

GridSplitterは、ドラッグ操作またはキーボード操作によってGrid内の列または行の幅・高さを動的に変更するコントロールを提供する。Thumbを継承し、ドラッグイベントを処理してグリッド定義のサイズを更新する。

**業務上の目的・背景**：ユーザーがアプリケーションのレイアウトをカスタマイズしたいというニーズは一般的である。例えば、エディタのサイドパネル幅の調整、マスター・ディテール画面の分割比率変更などがある。GridSplitterは、このようなユーザー主導のレイアウト調整機能を提供する。

**機能の利用シーン**：
- IDE風のマルチペインレイアウト
- ファイルエクスプローラー風のサイドパネル
- データビューワーの列幅調整
- レスポンシブダッシュボード

**主要な処理内容**：
1. ドラッグ操作による列/行サイズの変更
2. キーボード（矢印キー）による移動
3. プレビュー機能（ShowsPreview）
4. Star/Pixel両方のサイズ単位対応
5. MinWidth/MaxWidth制約の考慮

**関連システム・外部連携**：Thumbを継承し、Grid内に配置して使用。

**権限による制御**：特になし。UIコンポーネントとしてアプリケーション側で必要に応じて実装。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | GridSplitterはGrid分割が必要な画面で使用される |

## 機能種別

ユーザー操作 / レイアウト調整

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| ResizeDirection | GridResizeDirection | No | リサイズ方向（デフォルト: Auto） | Auto/Columns/Rows |
| ResizeBehavior | GridResizeBehavior | No | リサイズ対象（デフォルト: BasedOnAlignment） | BasedOnAlignment/CurrentAndNext/PreviousAndCurrent/PreviousAndNext |
| ShowsPreview | bool | No | プレビュー表示（デフォルト: false） | - |
| KeyboardIncrement | double | No | キーボード移動量（デフォルト: 10） | 正数 |
| DragIncrement | double | No | ドラッグ移動の丸め単位（デフォルト: 1） | 正数 |
| PreviewContent | ITemplate<Control> | No | プレビューコンテンツ | - |

### GridResizeDirection列挙型

| 値 | 説明 |
|-----|------|
| Auto | Alignmentと幅/高さ比から自動判定 |
| Columns | 列幅をリサイズ |
| Rows | 行高をリサイズ |

### GridResizeBehavior列挙型

| 値 | 説明 |
|-----|------|
| BasedOnAlignment | Alignmentから自動判定 |
| CurrentAndNext | 現在と次の列/行をリサイズ |
| PreviousAndCurrent | 前と現在の列/行をリサイズ |
| PreviousAndNext | 前と次の列/行をリサイズ |

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 更新されたGridLength | GridLength | リサイズ後の列/行サイズ |
| カーソル | Cursor | リサイズ方向に応じたカーソル変更 |

## 処理フロー

### 処理シーケンス

```
1. OnDragStarted
   ├─ InitializeData（リサイズデータ初期化）
   │   ├─ GetParentGrid
   │   ├─ GetEffectiveResizeDirection
   │   ├─ GetEffectiveResizeBehavior
   │   └─ SetupDefinitionsToResize
   └─ SetupPreviewAdorner（ShowsPreview時）
2. OnDragDelta（ドラッグ中）
   ├─ DragIncrementで丸め
   ├─ ShowsPreview時: Adorner位置更新
   └─ ShowsPreview以外: MoveSplitter直接実行
3. OnDragCompleted
   ├─ ShowsPreview時: MoveSplitter実行
   └─ _resizeDataクリア
```

### フローチャート

```mermaid
flowchart TD
    A[OnDragStarted] --> B[GetParentGrid]
    B --> C{Grid存在?}
    C -->|No| D[終了]
    C -->|Yes| E[InitializeData]
    E --> F[SetupDefinitionsToResize]
    F --> G{設定成功?}
    G -->|No| D
    G -->|Yes| H{ShowsPreview?}
    H -->|Yes| I[SetupPreviewAdorner]
    H -->|No| J[OnDragDelta待機]
    I --> J
    J --> K[OnDragDelta]
    K --> L{ShowsPreview?}
    L -->|Yes| M[Adorner位置更新]
    L -->|No| N[MoveSplitter]
    M --> O{ドラッグ継続?}
    N --> O
    O -->|Yes| K
    O -->|No| P[OnDragCompleted]
    P --> Q{ShowsPreview?}
    Q -->|Yes| R[MoveSplitter + RemoveAdorner]
    Q -->|No| S[終了]
    R --> S
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-01 | Auto方向判定 | HorizontalAlignment!=Stretchの場合Columns、VerticalAlignment!=Stretchの場合Rows、それ以外はWidth<=HeightでColumns | ResizeDirection=Auto時 |
| BR-40-02 | Star/Pixel混在 | 両方Starの場合は両方リサイズ、片方Pixelの場合はPixel側のみリサイズ | 常時 |
| BR-40-03 | MinWidth/MaxWidth制約 | リサイズ時にMin/Max制約を超えない | 常時 |
| BR-40-04 | キャンセル | Escapeキーでリサイズをキャンセルし元に戻す | リサイズ中 |

### 計算ロジック

**SplitBehavior判定**:
```
if (definition1.IsStar && definition2.IsStar)
    SplitBehavior = Split  // 両方リサイズ
else if (!definition1.IsStar)
    SplitBehavior = Resize1  // definition1のみ
else
    SplitBehavior = Resize2  // definition2のみ
```

**Delta制約**:
```
minDelta = -Min(definition1Len - definition1Min, definition2Max - definition2Len)
maxDelta = Min(definition1Max - definition1Len, definition2Len - definition2Min)
```

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

該当なし（UIコンポーネントのためDB操作は行わない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | リサイズ不可 | RowSpan/ColumnSpan > 1 | リサイズを行わない |
| - | リサイズ不可 | 対象定義が範囲外 | リサイズを行わない |
| - | キャンセル | 合計サイズが変化した場合 | 元の長さに戻す |

## パフォーマンス要件

- ドラッグ中のスムーズな更新
- UseLayoutRoundingによるピクセル境界への丸め

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

特になし

## 備考

- WPFのGridSplitterからの移植実装
- ItemsControl内でも動作（ContentPresenter経由の場合も対応）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | GridResizeDirection列挙型（905-922行目） |
| 1-2 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | GridResizeBehavior列挙型（927-948行目） |
| 1-3 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | SplitBehavior列挙型（836-852行目） |
| 1-4 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | ResizeDataクラス（857-899行目） |

**読解のコツ**: ResizeDataクラスにリサイズ操作中の全状態が格納される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | メインクラス |

**主要処理フロー**:
- **23行目**: `GridSplitter`クラス定義。`Thumb`を継承
- **28-59行目**: プロパティ定義（ResizeDirection/ResizeBehavior/ShowsPreview/KeyboardIncrement/DragIncrement/PreviewContent）
- **61-62行目**: カーソル定義（Columns用/Rows用）
- **64行目**: `_resizeData` - リサイズ操作中のデータ

#### Step 3: 方向・動作の自動判定

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | GetEffectiveResizeDirection（124-152行目） |
| 3-2 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | GetEffectiveResizeBehavior（157-196行目） |

**主要処理フロー**:
- **133-149行目**: Auto判定 - HorizontalAlignment/VerticalAlignment/Width vs Heightで決定
- **161-193行目**: BasedOnAlignment判定 - Alignmentに基づきPreviousAndCurrent/CurrentAndNext/PreviousAndNextを決定

#### Step 4: ドラッグイベント処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | OnDragStarted（462-475行目） |
| 4-2 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | OnDragDelta（477-513行目） |
| 4-3 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | OnDragCompleted（515-530行目） |

**主要処理フロー**:
- **474行目**: InitializeData呼び出し
- **487-489行目**: DragIncrementで丸め
- **491-511行目**: ShowsPreview分岐
- **524行目**: MoveSplitter実行

#### Step 5: 初期化とリサイズ実行

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | InitializeData（213-244行目） |
| 5-2 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | SetupDefinitionsToResize（249-326行目） |
| 5-3 | GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | MoveSplitter（699-750行目） |

**主要処理フロー**:
- **223-231行目**: ResizeData初期化
- **264-283行目**: index1/index2計算（PreviousAndCurrent/CurrentAndNext/PreviousAndNext）
- **307-319行目**: SplitBehavior決定
- **709-714行目**: UseLayoutRounding時のdelta丸め
- **730-738行目**: 合計サイズ変化検出時のキャンセル

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

```
GridSplitter (Thumb継承)
    │
    ├─ OnDragStarted()
    │      └─ InitializeData()
    │             ├─ GetParentGrid()
    │             ├─ GetEffectiveResizeDirection()
    │             ├─ GetEffectiveResizeBehavior()
    │             ├─ SetupDefinitionsToResize()
    │             │      ├─ GetGridDefinition()
    │             │      └─ IsStar()
    │             └─ SetupPreviewAdorner()
    │
    ├─ OnDragDelta()
    │      ├─ DragIncrement丸め
    │      ├─ ShowsPreview: Adorner.Offset更新
    │      └─ MoveSplitter()
    │             ├─ GetDeltaConstraints()
    │             └─ SetLengths()
    │
    ├─ OnDragCompleted()
    │      ├─ MoveSplitter()
    │      └─ RemovePreviewAdorner()
    │
    └─ OnKeyDown()
           └─ KeyboardMoveSplitter()
                  ├─ InitializeData()
                  └─ MoveSplitter()
```

### データフロー図

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

ResizeDirection ─────────▶ GetEffectiveResizeDirection()
HorizontalAlignment ─────▶ │
VerticalAlignment ───────▶ │
                              │
                              ▼
                     direction判定
                              │
ResizeBehavior ──────────▶ GetEffectiveResizeBehavior()
                              │
                              ▼
                     behavior判定
                              │
                              ▼
Grid.ColumnDefinitions ──▶ SetupDefinitionsToResize()
Grid.RowDefinitions ─────▶ │
                              │
                              ▼
                     definition1/definition2決定
                              │
ドラッグdelta ────────────▶ MoveSplitter()
                              │
                    ┌─────────┴─────────┐
                    ▼                   ▼
         GetDeltaConstraints()   SetLengths()
                    │                   │
                    └─────────┬─────────┘
                              ▼
                     GridLength更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| GridSplitter.cs | `src/Avalonia.Controls/GridSplitter.cs` | ソース | メインクラス、列挙型、ResizeData |
| Thumb.cs | `src/Avalonia.Controls/Primitives/Thumb.cs` | ソース | 基底クラス |
| Grid.cs | `src/Avalonia.Controls/Grid.cs` | ソース | 親Gridコントロール |
| DefinitionBase.cs | `src/Avalonia.Controls/DefinitionBase.cs` | ソース | 行/列定義基底 |
| ColumnDefinition.cs | `src/Avalonia.Controls/ColumnDefinition.cs` | ソース | 列定義 |
| RowDefinition.cs | `src/Avalonia.Controls/RowDefinition.cs` | ソース | 行定義 |
| AdornerLayer.cs | `src/Avalonia.Controls/Primitives/AdornerLayer.cs` | ソース | プレビュー表示用 |
