# Alert

> 模态对话框，包含标题、消息正文和一到多个操作按钮，会阻断当前交互。

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

# Alert

`Alert` 是 CrossFly 的模态对话框——相当于 `MessageBox`/`AlertDialog`。它显示一个**标题**、一条**消息**（自动换行）以及一个或多个按行排列的**操作按钮**。每个按钮都可以有自己的回调；没有回调时，按钮只是关闭对话框。视觉效果（颜色、字体、按钮布局）来自当前激活的 `Skin`。

> 🟢 · **包：** `dev.crossfly.ui` · **继承：** `Window` · **样式：** `AlertStyle` + `WindowStyle`

## 何时使用
- 需要用户确认的确认操作（删除？保存？丢弃？）。
- 简单的是/否选择，或需要一个“确定”的消息。

## 何时不要使用
- 被动且短暂的反馈（成功/错误/状态） → 使用 [`Hint`](/docs/ui/widgets/hint)。
- 针对某个条目的上下文操作列表（分享/编辑/删除） → 使用 [`ActionSheet`](/docs/ui/widgets/actionsheet)。

## 外观
居中的卡片，顶部为标题，中间为消息，底部为一行按钮。默认情况下背景**不会**变暗——调整 `WindowStyle.fadeBackground` 以获得经典的模态效果。

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

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

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

      Button abrir = new Button("Abrir alerta");
      abrir.onAction(c ->
      {
         Alert a = new Alert("Excluir item",
            "Tem certeza que deseja excluir este item? Esta ação não pode ser desfeita.",
            "Excluir", "Cancelar");
         // 背景变暗以强化模态感 (alpha 0..255)
         WindowStyle ws = a.getStyle();
         ws.fadeBackground(150);
         // 按钮 0 = 删除 -> 执行、关闭并给出反馈
         a.buttonAction(0, origin ->
         {
            a.hide();
            Hint.show("Item excluído", 2000);
         });
         // 按钮 1 = 取消：无回调 -> 默认已调用 hide()
         a.show();
      });
      add(LAY_AUTO, abrir);
   }
}
```

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

## 基本用法
```java
// buttonTexts 是可选的可变参数 — 若为空/null，则变为 {"OK"}
Alert a = new Alert("Aviso", "Operação concluída com sucesso.", "OK");
a.show();
```

## 构造函数
| 构造函数 | 描述 |
|---|---|
| `Alert(String title, String message, String... buttonTexts)` | `title` 可以是 `""`（视觉上隐藏标题）。`buttonTexts` 可选；为空/`null` → `{"OK"}`。 |

> ⚠️ **不存在静态的 `Alert.show(...)`，也没有会触发回调的构造函数。** 始终用 `new Alert(...)` 实例化，为每个按钮注册回调，然后调用 `show()`。

## 属性与自定义
| 属性 / 方法 | 类型 | 默认值 | 作用 |
|---|---|---|---|
| `style()` | `AlertStyle` | skin | 返回当前的 `AlertStyle`。 |
| `style(AlertStyle)` | 流式 | — | 设置自定义的 `AlertStyle`。 |
| `style().buttonLayout(...)` | `IOS`/`FLUTTER`/`MATERIAL` | `MATERIAL` | 按钮的排列样式。 |
| `style().maxButtonsPerRow(int)` | `int` | skin | 每行按钮数；超出的换到新行。 |
| `skin(Skin)` | 流式 | — | 切换 skin 并重新更新 `AlertStyle`。 |
| `getButton(int index)` | `Button` | — | `show()` 之后的按钮（在此之前为 `null`）。 |
| `getAutoWidth()` / `getAutoHeight()` | `int` | — | 计算得出的尺寸。 |

## 回调与事件
| 回调 | 触发时机 |
|---|---|
| `buttonAction(int index, Component.Action action)` | 点击索引为 `index` 的按钮时（从 0 开始）。 |

> ⚠️ **响应不通过 `show(onAnswer)` 传回。** `Alert` 继承自 `Window` 而非 `Picker`——而 `Window.hide()` 只有在 `this instanceof Picker` 时才会传递 `onAnswer`。因此，要对每个按钮做出响应，请使用 `buttonAction(index, action)`。`Alert` 中没有 `getAnswer()`。

回调的类型是 `Component.Action`，其方法为 `void run(Component origin)`——lambda 体**不返回值**（不要写 `return null;`）。

## 状态
- 未注册回调的按钮 → 接收默认动作 `hide()`（关闭对话框）。
- 有回调的按钮 → 执行回调；如果回调不关闭，对话框会保持打开（请自己调用 `a.hide()`）。
- “返回”/back 手势 → 若 `WindowStyle.canHide`（默认 `true`）则被尊重。

### 带变暗背景的确认
```java
Alert confirm = new Alert("Excluir item",
   "Tem certeza que deseja excluir este item? Esta ação não pode ser desfeita.",
   "Excluir", "Cancelar");

// 背景变暗以强化模态感 (alpha 0..255)
WindowStyle ws = confirm.getStyle();
ws.fadeBackground(150);

// 按钮 0 = 删除 -> 执行、关闭并给出反馈
confirm.buttonAction(0, origin ->
{
   excluirItem();
   confirm.hide();
   Hint.show("Item excluído", 2000);
});
// 按钮 1 = 取消：无回调 -> 默认已调用 hide()

confirm.show();
```

### 选择按钮布局
```java
Alert a = new Alert("Salvar", "Deseja salvar as alterações?", "Salvar", "Descartar");
a.style().buttonLayout(AlertStyle.ButtonLayout.IOS)  // IOS / FLUTTER / MATERIAL
         .maxButtonsPerRow(2);
a.show();
```

## 最佳实践
- 用于需要确认的决策。对于被动反馈，优先使用 [`Hint`](/docs/ui/widgets/hint)。
- 保持按钮数量较少——太多会换行并使对话框变高。
- 每当想要经典模态效果时，配合 `fadeBackground` 使用。

## 常见错误
- **期望通过 `show(onAnswer)` 获得响应** —— 那是 `Picker` 专属的。请注册 `buttonAction(...)`。
- **在 `show()` 之前访问 `getButton(i)`** —— 按钮在 `onAdd` 中创建，在此之前返回 `null`。
- **让 `buttonAction` 的 lambda 返回值** —— `Action.run(...)` 是 `void`。

## 另见
- 该领域指南：[`ui/`](/docs/ui) · 食谱：[`cookbook/`](/docs/cookbook)
- 相关组件：[`ActionSheet`](/docs/ui/widgets/actionsheet) · [`Hint`](/docs/ui/widgets/hint)
