# ListViewWindow

> Modal popup that wraps a ListView in a Window for selection in a floating window.

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

# ListViewWindow

`ListViewWindow` wraps a [`ListView`](/docs/ui/widgets/listview) inside a `Window` for **modal popup** behavior (centered floating selector). In `SINGLE` mode, tapping an item selects and **closes** the window; in `MULTIPLE`, the "Confirm" button closes it.

Most methods simply **delegate** to the internal `ListView` (configuration and selection query). Use `getPanel()` if you want the embedded `ListView` without the popup.

> 🟡 · **Package:** `dev.crossfly.ui` · **Extends:** `Window` · **Style:** `WindowStyle` (via `selectionWindowStyle()`)

## When to use
- A modal choice that interrupts the flow (actions menu, item selector, options confirmation).
- You want automatic centering and screen-relative sizing without building the `Window` by hand.

## When NOT to use
- A list embedded in a screen (not modal) → use [`ListView`](/docs/ui/widgets/listview) directly (or `getPanel()`).
- Thousands of items → use [`VirtualListView`](/docs/ui/widgets/virtuallistview) inside your own `Window`.

## Appearance
Centered window occupying a fraction of the screen (`widthRatio`/`heightRatio`, default `0.5`/`0.5`), containing the `ListView` (header + scrollable items + button in `MULTIPLE`). The background darkens according to the `WindowStyle`/`Skin`.

## Complete example (copy, paste and run)
```java
package com.exemplo;

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

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // The ListViewWindow is a Window (popup): opens with show(), doesn't enter the layout.
      Button abrir = new Button("Escolher vibração");
      abrir.onAction(e -> {
         ListViewWindow pm = new ListViewWindow("Vibrar",
            new String[]{"Curto", "Longo", "Padrão"}, ListViewWindow.ItemType.SINGLE);
         pm.onConfirm(c -> System.out.println("idx = " + pm.getSelectedIndex()));
         pm.show();
      });

      add(.9f, abrir);   // the button that opens the popup enters the layout
   }
}
```

> The snippets below show **only the core** (what goes inside `onShow()`). To run them, place them in the `MainWindow` skeleton above.

## Basic usage
```java
// Menu popup (SINGLE): tapping closes and reports the index
ListViewWindow pm = new ListViewWindow("Vibrar", new String[]{"Curto", "Longo", "Padrão"},
      ListViewWindow.ItemType.SINGLE);
pm.onConfirm(c -> System.out.println("idx = " + pm.getSelectedIndex()));
pm.show();
```

## Constructors
| Constructor | Description |
|---|---|
| `ListViewWindow(String[] items, ItemType itemType)` | Popup without title |
| `ListViewWindow(String title, String[] items, ItemType itemType)` | Popup with title in the header |

`ListViewWindow.ItemType` is its **own** enum (`SINGLE`, `MULTIPLE`) that internally converts to `ListView.ItemType`.

## Properties and customization
| Property / method | Type | Default | What it does |
|---|---|---|---|
| `size(float widthRatio, float heightRatio)` | `ListViewWindow` | `0.5, 0.5` | Popup size as a fraction of the screen |
| `getPanel()` | `ListView` | — | Access to the internal `ListView` |
| `ItemFactory(ItemFactory)` | `ListViewWindow` | `null` | Custom row factory (note the **capital** initial) |
| `computeIdealHeight(int)` | `int` | — | Delegates to `panel.computeIdealHeight` |
| `title/confirmText/itemStyle/confirmButtonStyle/headerStyle` | `ListViewWindow` | theme | Fluent delegations to the `ListView` |

### Selection queries (delegated)
`getSelectedCount()`, `getSelectedIndex()`, `getSelectedText()`, `getSelectedIndices()`, `getSelectedTexts()`, `isSelected(int)`, `getSelectionState()`.

## Callbacks and events
| Callback | When it fires |
|---|---|
| `onConfirm(Component.Action)` | `SINGLE`: on tapping an item (and closes). `MULTIPLE`: on tapping "Confirm" (and closes) |

## States
Like any `Window`: use `show()` to display and `hide()` to close. The internal `ListView` already points its `closeAction` to `this::hide`, so the selection closes the popup automatically.

### Popup with row factory (image + label)
```java
Button abrir = new Button("Lista de imagens");
abrir.onAction(e -> {
   ListViewWindow w = new ListViewWindow("Imagens", labels, ListViewWindow.ItemType.SINGLE);
   w.ItemFactory((text, index) -> {
      Image img = new Image(paths[index]);
      ImageView iv = new ImageView(img, UIConstants.ImageSize.RATIO);
      Component row = new Component().style(Styles.component()
         .pressable().ingap(Gap.ofFont(.3f)).autoHeight(Relative.ofFont(3f)));
      row.setLayout(new GridLayout(Position.START, Position.CENTER));
      row.add(.22f, .78f, iv, new Label(text));
      return row;
   });
   w.show();
});
```

### Multi-selection with custom size
```java
ListViewWindow w = new ListViewWindow("Tópicos",
      new String[]{"Java", "Kotlin", "Dart"}, ListViewWindow.ItemType.MULTIPLE)
   .size(.7f, .6f)
   .confirmText("OK");
w.onConfirm(c -> System.out.println(w.getSelectedCount() + " marcados"));
w.show();
```

## Best practices
- Adjust `size(...)` to the content — many items call for a larger `heightRatio`; few, smaller.
- To embed the same list without a popup, call `getPanel()` and add the returned `ListView` to your layout.

## Common mistakes
- **Confusing `ItemFactory(...)` (method, capital `I`) with the `ListView`'s `itemFactory(...)`** — here the fluent method starts with a capital by the class's convention.
- **Using the wrong enum** — in `ListViewWindow` pass `ListViewWindow.ItemType`, not `ListView.ItemType`.

## See also
- Area guide: [`ui/`](/docs/ui) · Recipes: [`cookbook/`](/docs/cookbook)
- Related components: [`ListView`](/docs/ui/widgets/listview) · [list items](/docs/ui/widgets/list-items)
