# ActionSheet

> 锚定在屏幕底部的 iOS 风格操作表，用于让用户从若干动作中选择一个。

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

# ActionSheet

`ActionSheet` 是 iOS 风格的操作表：一张圆角卡片锚定在**屏幕底部**，包含一组选项按钮以及下方单独的**取消**按钮。当选项过多放不下时，它们会分布到多列（最多 `maxColumns` 列）。操作表背后有一层灰色“遮罩”（`fullscreenColor = 0xD0DDDDDD`），营造模态效果。

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

## 何时使用
- 针对某个条目的上下文操作菜单（分享、重命名、删除……）。
- 当你想在视觉上突出某个破坏性操作（红色）时，如 iOS 的模式。

## 何时不要使用
- 简单的是/否选择或带消息的确认 → 使用 [`Alert`](/docs/ui/widgets/alert)。
- 被动/短暂的反馈 → 使用 [`Hint`](/docs/ui/widgets/hint)。

## 外观
底部卡片，宽度约为屏幕的 92%，选项纵向堆叠，用细线分隔，取消按钮为完全圆角，紧接在下方。屏幕背景被一层灰色遮罩覆盖。

## 完整示例（复制、粘贴并运行）
```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 folha de ações");
      abrir.onAction(c ->
      {
         ActionSheet sheet = new ActionSheet("Cancelar", "Compartilhar", "Renomear", "Excluir");
         sheet.buttonAction(0, origin -> { sheet.hide(); Hint.show("Compartilhar", 2000); });
         sheet.buttonAction(1, origin -> { sheet.hide(); Hint.show("Renomear", 2000); });
         sheet.buttonAction(2, origin -> { sheet.hide(); Hint.show("Excluído", 2000); });
         // 破坏性操作用红色
         sheet.buttonStyle(2, ActionSheet.createDefaultButtonStyle().textColor(0xFFFF0000));
         sheet.show();
      });
      add(LAY_AUTO, abrir);
   }
}
```

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

## 基本用法
```java
ActionSheet sheet = new ActionSheet("Cancelar", "Compartilhar", "Renomear", "Excluir");
sheet.buttonAction(0, origin -> { compartilhar(); sheet.hide(); });
sheet.buttonAction(1, origin -> { renomear();     sheet.hide(); });
sheet.buttonAction(2, origin -> { excluir();      sheet.hide(); });
sheet.show();
```

## 构造函数
| 构造函数 | 描述 |
|---|---|
| `ActionSheet(String cancelText, String... buttonTexts)` | `cancelText`（默认 `"Cancel"`）和各选项的文本（为空/`null` → `{"OK"}`）。 |

## 属性与自定义
| 属性 / 方法 | 类型 | 默认值 | 作用 |
|---|---|---|---|
| `buttonStyle(ComponentStyle)` | 流式 | skin | **所有**选项的默认样式。 |
| `buttonStyle(int index, ComponentStyle)` | 流式 | — | 覆盖某个特定选项的样式。 |
| `buttonStyle(int index)` | `ComponentStyle` | — | 该选项解析后的样式。 |
| `cancelStyle(ComponentStyle)` | 流式 | skin | 取消按钮的样式。 |
| `containerStyle(ComponentStyle)` | 流式 | skin | 卡片的样式（边框圆角）。 |
| `maxColumns(int)` | 流式 | `1` | 溢出时的最大列数（>=1）。 |
| `getButton(int)` / `getCancelButton()` | `Button` | — | `show()`/`onAdd` 之后的按钮。 |
| `createDefaultButtonStyle()` / `createDefaultCancelStyle()` / `createDefaultContainerStyle()` | `ComponentStyle`（静态） | — | 默认样式的工厂方法。 |

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

> 由于 `ActionSheet` 继承自 `Window`（而非 `Picker`），**响应通过这些回调传回**，而不是通过 `show(onAnswer)`。没有 `getAnswer()`。`Component.Action` 的 lambda 是 `void run(Component origin)`——不要返回值。

## 状态
- 选项**没有**回调 → 接收默认动作 `hide()`（关闭操作表）。
- 选项**有**回调 → 执行回调；如果它不调用 `hide()`，操作表保持打开。
- 点击遮罩（操作表外部）或返回 → 当 `canHide` 处于激活状态时（默认）关闭。

### 突出破坏性操作并允许 2 列
```java
ActionSheet sheet = new ActionSheet("Cancelar", "Compartilhar", "Renomear", "Excluir");

sheet.buttonAction(0, origin -> { compartilhar(); sheet.hide(); });
sheet.buttonAction(1, origin -> { renomear();     sheet.hide(); });
sheet.buttonAction(2, origin -> { excluir();      sheet.hide(); });

// 破坏性选项用红色
sheet.buttonStyle(2, ActionSheet.createDefaultButtonStyle().textColor(0xFFFF0000));

// 如果选项很多，允许最多 2 列
sheet.maxColumns(2);

sheet.show();
```

## 最佳实践
- 用于一组上下文操作；对于简单的是/否，优先使用 [`Alert`](/docs/ui/widgets/alert)。
- 用 `buttonStyle(index, ...)` 以红色突出破坏性操作。
- 对于较长的列表，配置 `maxColumns`，使操作表不超出屏幕。

## 常见错误
- **回调中没有 `hide()`** —— 操作表会保持打开；自动 `hide()` 仅在**没有**注册回调时发生。
- **让 lambda 返回值** —— `Component.Action.run(...)` 是 `void`。
- **在 `show()` 之前访问 `getButton(i)`** —— 按钮在 `onAdd` 中创建。

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