# 对话框与模态框

> Alert、ActionSheet 和 Hint——如何打开、获取响应以及配置外观。

Source: https://crossfly.dev/zh/docs/ui/navigation/dialogs-and-modals — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 对话框与模态框

对话框是堆叠在当前屏幕之上的**弹出窗口**。在 CrossFly 中，三个主要类型是
`Alert`（确认）、`ActionSheet`（操作列表）和 `Hint`（短暂提示，即旧的 "Toast"）。

## ⚠️ `Alert`/`ActionSheet` 通过按钮响应，而不是通过 `onAnswer`
它们继承自 `Window`（而非 `Picker`），因此 **`show(callback)` 不返回值**。用户的选择通过**按钮的回调**
（`buttonAction`）传入。（参见[返回值](/docs/ui/navigation/returning-results)。）

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

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

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

      // Alert——构造函数中的按钮（0 = 取消，1 = 删除）；按索引触发动作。
      Button excluir = new Button("Excluir item");
      excluir.onAction(c -> {
         Alert a = new Alert("Excluir", "Confirma a exclusão deste item?", "Cancelar", "Excluir");
         a.buttonAction(1, x -> Hint.show("Item excluído", 2000));   // 取消（0）只关闭
         // 弹窗：通过流式 WindowStyle 调整尺寸并使背景变暗
         a.getStyle().widthRatio(0.85f).fadeBackground(128).transition(UIConstants.Transition.FADE);
         a.show();
      });
      add(LAY_AUTO, excluir);

      // ActionSheet——第 1 个参数是取消文本。
      Button compartilhar = new Button("Compartilhar");
      compartilhar.onAction(c -> {
         ActionSheet sheet = new ActionSheet("Cancelar", "E-mail", "Mensagem");
         sheet.buttonAction(0, x -> Hint.show("Enviado por e-mail", 2000));   // 索引 0 = 电子邮件
         sheet.buttonAction(1, x -> Hint.show("Enviado por SMS", 2000));      // 索引 1 = 消息
         sheet.show();
      });
      add(LAY_AUTO, compartilhar);

      // Hint——短暂提示（延迟以毫秒计）。
      Button avisar = new Button("Mostrar aviso");
      avisar.onAction(c -> Hint.show("Salvo com sucesso", 2000));
      add(LAY_AUTO, avisar);
   }
}
```
> 下面的片段**仅展示核心部分**（放入 `onShow()` 内的内容）。要运行它们，请放入上面的 `MainWindow` 骨架中。

## Alert（确认 / 提示）
按钮的文本放在**构造函数**中；每个按钮的动作通过**索引**用 `buttonAction` 绑定：
```java
// 按钮：索引 0 = “取消”，索引 1 = “删除”
Alert a = new Alert("Excluir", "Confirma a exclusão deste item?", "Cancelar", "Excluir");
a.buttonAction(1, c -> excluirItem());   // 动作来自这里，而非 show(cb)；取消只关闭
a.show();
```

## ActionSheet（选择一个操作）
同样在构造函数中接收文本（**第一个**参数是取消的文本）：
```java
ActionSheet sheet = new ActionSheet("Cancelar", "E-mail", "Mensagem");
sheet.buttonAction(0, c -> enviarPorEmail());   // 索引 0 = “电子邮件”
sheet.buttonAction(1, c -> enviarPorSMS());     // 索引 1 = “消息”
sheet.cancelAction(c -> {});                     // 可选：取消时的动作
sheet.show();
```

## Hint（短暂提示——取代 Toast/ToolTip）
```java
Hint.show("Salvo com sucesso", 2000);     // 自行消失
```

## 配置弹窗外观——`WindowStyle`
弹窗的尺寸/位置由窗口的 `WindowStyle` 决定。常用字段：

| 字段 | 效果 |
|---|---|
| `widthRatio` / `heightRatio` | 尺寸作为屏幕的比例 |
| `yRatio` / `xAbs` / `yAbs` | 位置 |
| `fadeBackground` | 使弹窗后方的背景变暗 |
| `canHide` | 点击外部关闭弹窗 |
| `canMove` | 用户可以拖动 |
| `transition` / `transitionDuration` | 进入/退出动画（默认 `NONE`、`0.5s`） |

```java
getStyle().widthRatio = 0.85f;                       // 宽度的 85%
getStyle().fadeBackground = true;                     // 变暗的背景
getStyle().transition = UIConstants.Transition.FADE;  // 以淡入出现
```

## 最佳实践
- 使用清晰的按钮标签（用"Excluir"而非"OK"）；破坏性操作放最后。
- 对于非阻塞式确认，优先使用 `Hint` 而非 `Alert`。

## 常见错误
- **期望 `alert.show(cb)` 触发 `cb`**——它不会触发；用 `buttonAction(i, callback)` 绑定动作。
- 调用 `addButton(...)`/`addOption(...)`——它们不存在；文本放在**构造函数**中，动作通过**索引**绑定。
- 在同一事件上堆叠多个弹窗。

## 另见
- [返回值](/docs/ui/navigation/returning-results) · [窗口栈](/docs/ui/navigation/the-window-stack)
- 目录：[Alert](/docs/ui/widgets/alert) · [ActionSheet](/docs/ui/widgets/actionsheet) · [Hint](/docs/ui/widgets/hint)
