# 如何显示模态选择弹窗

> 将列表作为居中的模态窗口打开，通过 ItemFactory 使用自定义行。

Source: https://crossfly.dev/zh/docs/cookbook/lists/popup-de-selecao — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何显示模态选择弹窗

**目的。** 将选择列表作为**浮动窗口**打开（而非嵌入到布局中）。
`ListViewWindow` 将 `ListView` 包裹在 `Window` 中，将其在屏幕上居中，并在用户选择时
自动关闭。既适用于简单选择，也适用于外观自定义的行。

## 使用的 API
- `new ListViewWindow(String[] items, ListViewWindow.ItemType type)` — 创建弹窗；还有一个带 `String title` 的重载。
- `ListViewWindow.ItemType.SINGLE` / `MULTIPLE` — `ListViewWindow` 的**专属枚举**（`ListView.ItemType` 的别名，内部由 `toPanel()` 转换）。
- `ListViewWindow.size(float widthRatio, float heightRatio)` — 尺寸为屏幕的比例（默认 `0.5f`、`0.5f`）；弹窗始终居中。
- `ListViewWindow.onConfirm(Component.Action)` — 委托给内部的 `ListView`；在 SINGLE 模式下确认时，窗口通过 `closeAction(this::hide)` 自动关闭。
- `ListViewWindow.ItemFactory(ItemFactory factory)` — **注意：方法名首字母大写**。接口为 `ItemFactory { Component create(String text, int index) throws Exception; }`。
- `ListViewWindow.show()` / `hide()` — 继承自 `Window`（在 `EventsProcessor` 中入栈/出栈）。
- `ListViewWindow.getSelectedText()` / `getSelectedIndex()` / `getSelectedTexts()` — 委托给内部面板。
- `ListViewWindow.getPanel()` — 访问底层的 `ListView`，如需高级配置。

## 完整示例（复制、粘贴并运行）
```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());

      final String[] paises = {"Brasil", "Portugal", "Angola", "Moçambique"};

      // ListViewWindow 是弹窗：不进入布局 — 由 Button 触发 show() 打开
      Button abrir = new Button("Escolher país");
      abrir.onClick(e ->
      {
         ListViewWindow popup = new ListViewWindow("País", paises, ListViewWindow.ItemType.SINGLE);
         popup.size(.6f, .5f);                 // 屏幕宽度的 60%，高度的 50%
         popup.onConfirm(ev ->
         {
            String pais = popup.getSelectedText();   // SINGLE 模式下窗口会自动关闭
            Debug.log("País: " + pais);
            return null;
         });
         popup.show();                          // 打开居中的模态框
         return null;
      });

      add(LAY_AUTO, abrir);
   }
}
```

> 下面的片段只展示**核心部分**。要运行它，请将其放入上面的 `MainWindow` 骨架中。

## 最小片段
```java
// 已在源码核实：ListViewWindow.java
String[] paises = {"Brasil", "Portugal", "Angola", "Moçambique"};

ListViewWindow popup = new ListViewWindow("País", paises, ListViewWindow.ItemType.SINGLE);
popup.size(.6f, .5f);                 // 屏幕宽度的 60%，高度的 50%
popup.onConfirm(e ->
{
   String pais = popup.getSelectedText();   // SINGLE 模式下窗口会自动关闭
   return null;
});
popup.show();                          // 打开居中的模态框
```

使用 `ItemFactory` 的自定义行（注意方法名中的 **`I` 大写**）：

```java
// 已在源码核实：ListViewWindow.ItemFactory（接口）+ ItemFactory(...) 方法
ListViewWindow popup = new ListViewWindow("Contato", nomes, ListViewWindow.ItemType.SINGLE);
popup.ItemFactory((text, index) ->
{
   Component linha = new Component();
   linha.setLayout(new GridLayout());
   linha.add(new Label(text));                 // 按你所需构建该行
   return linha;
});
popup.onConfirm(e -> { /* ... */ return null; });
popup.show();
```

> Javadoc 与代码的差异：类的 Javadoc 展示了在 `ListView` 上调用 `sel.show()`，但
> `show()`/`hide()` 存在于 `Window` 中——因此在 `ListViewWindow` 上，而不是在 `ListView` 上。
> 弹窗模式请始终使用 `ListViewWindow`。

## 深入了解
- 同一列表的嵌入版本（无窗口）：[lista-simples.md](/docs/cookbook/lists/lista-simples)。
- 组件指南：[ListViewWindow](/docs/ui/widgets/listviewwindow) · 权威来源：第 15 章
