# 间距（Gap）

> 如何在不使用 Spacer 的情况下为组件设置间距 —— 使用 Gap、ingap/outgap 和布局分数。

Source: https://crossfly.dev/zh/docs/ui/widgets/spacing — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 间距（Gap）

> ⚠️ **`Spacer` 已在 2026 现代化改造中移除。** 不再有间距组件。要分隔组件，请按偏好顺序使用：**布局分数**（`LAY_AUTO`/`LAY_FILL`/分数）、组件的 **`ingap`/`outgap`**，或直接使用 `Gap` 值。

`Gap` 表示间距值（左、上、右、下），带可选缩放。它在整个 UI 中用作 **padding**（`ingap`）、**margin**（`outgap`），以及布局的内部间距。一个 `Gap` 可以是：

- **固定像素** —— `Gap.ofPixels(...)`
- **相对于字体** —— `Gap.ofFont(...)`（随 `font.scaledHeight()` 缩放，适合响应式）
- **相对于屏幕** —— `Gap.ofScreen(...)`（屏幕尺寸的百分比）

> 🟢 · **包：** `dev.crossfly.ui` · **继承：** `Component` · **相关：** `ComponentStyle.ingap`/`outgap`、布局

## 何时使用
- 定义组件的内部 padding（`ingap`）或外部 margin（`outgap`）。
- 向布局传递间距（例如单元格之间的间隙）。
- 用成比例的空间替代旧的 `Spacer`。

## 何时不要使用
- 要"推动"某个组件并吸收剩余空间 → 在 `VerticalLayout`/`HorizontalLayout` 中使用一行 `LAY_FILL`（见 [`layouts/`](/docs/ui)），而不是固定的 `Gap`。

## 完整示例（复制、粘贴并运行）
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;   // Styles, ButtonRole, State

/** 可执行的主界面。将 launcher 指向此类（classname）并运行：gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // 高度自然的表头
      add(LAY_AUTO, new Label("Cabeçalho"));

      // 通过 outgap（相对于字体的边距）使按钮与相邻元素隔开
      Button b = new Button("Enviar").style(
         Styles.theme().button(ButtonRole.PRIMARY, State.NORMAL)
            .pressable()
            .outgap(Gap.ofFont(0.5f)));   // 四边均为 0.5 × 字体高度
      add(LAY_AUTO, b);

      // 页脚被推到底部：LAY_FILL 那一行吸收垂直方向的剩余空间
      add(LAY_FILL, new Label("(espaço que sobra)"));
      add(LAY_AUTO, new Label("Rodapé"));
   }
}
```

> 下面的片段只展示**核心部分**（放入 `onShow()` 内的内容）。要运行它们，请将其放入上面的 `MainWindow` 骨架中。

### 1. 布局分数（推荐）
```java
VerticalLayout col = new VerticalLayout();
col.add(LAY_AUTO, cabecalho);   // 自然高度
col.add(LAY_FILL, conteudo);    // 吸收剩余空间（将页脚往下推）
col.add(LAY_AUTO, rodape);
```

### 2. 样式中的 `ingap` / `outgap`
```java
// 通过相对于字体的 outgap 设置外边距（使此组件与相邻元素隔开）
Button b = new Button("Enviar").style(
   Styles.theme().button(ButtonRole.PRIMARY, State.NORMAL)
      .pressable()
      .outgap(Gap.ofFont(0.5f)));   // 四边均为 0.5 × 字体高度
container.add(LAY_AUTO, b);
```

### 3. `Gap` 作为内部 padding
```java
Label card = new Label("Conteúdo do cartão").style(
   Styles.theme().label(LabelRole.BODY)
      .ingap(Gap.ofFont(1f, 0.5f, 1f, 0.5f)));  // L,T,R,B 以字体为单位
container.add(LAY_AUTO, card);
```

## 工厂方法（`Gap`）
| 工厂方法 | 缩放 | 用途 |
|---|---|---|
| `Gap.ofPixels(float all)` / `ofPixels(l, t, r, b)` | 固定像素 | 绝对值 |
| `Gap.ofFont(float all)` / `ofFont(l, t, r, b)` | × 默认字体高度 | **响应式**（推荐） |
| `Gap.ofFont(Font, l, t, r, b)` | × 指定字体高度 | 以特定字体为基准 |
| `Gap.ofScreen(float all)` / `ofScreen(l, t, r, b)` | 屏幕尺寸的 % | 与屏幕成比例的间距 |
| `Gap.none()` / `Gap.ZERO` | 零 | 无间距 |

## 属性与方法
| 方法 | 类型 | 作用 |
|---|---|---|
| `left()` / `top()` / `right()` / `bottom()` | `int` | 已解析的像素值（已应用缩放） |
| `width()` / `height()` | `int` | 水平合计（`left+right`）/ 垂直合计（`top+bottom`），单位像素 |
| `withFont(Font)` | `Gap` | 将系数以另一种字体为基准重算（若为字体相对） |
| `isFontRelative()` / `font()` | `boolean` / `Font` | 查询缩放基准 |

## 最佳实践
- 对于"剩余空间"，在布局中使用**单独一行** `LAY_FILL`，而不是多个 gap。
- 复用 `Gap.ZERO` 来清零 padding，无需分配对象。

## 常见错误
- **寻找 `Spacer` 类：** 它已不存在。请使用布局分数 / `Gap` / `ingap`/`outgap`。
- **对所有情况都使用 `Gap.ofPixels`：** 在不同密度的屏幕上间距会不一致；请优先使用 `ofFont`/`ofScreen`。
- **尝试用两个固定 gap 来居中：** 请使用布局的对齐方式或两侧的 `LAY_FILL`。

## 另请参阅
- 领域指南：[`ui/`](/docs/ui) · 食谱：[`cookbook/`](/docs/cookbook)
- 概念（约束、响应式）：[`concepts/`](/docs/concepts)
- 相关组件：[`Label`](/docs/ui/widgets/label) · [`Button`](/docs/ui/widgets/button)
