# 如何创建带显示/隐藏的密码字段

> 带掩码的密码字段，附可见性切换按钮，让用户随时确认自己输入的内容。

Source: https://crossfly.dev/zh/docs/cookbook/forms/campo-de-senha — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# 如何创建带显示/隐藏的密码字段

**用途。** 创建一个隐藏字符（显示为 `••••`）的字段，并可选地在右侧提供一个图标用于在显示和隐藏密码之间切换。

## 用到的 API
- `campo.<TextFieldStyle>getStyle().mode(EditMode.PASSWORD)` — 开启密码模式。字符显示为 `passwordChar`（默认 `'*'`；用 `.passwordChar('•')` 更改）。
- `campo.<TextFieldStyle>getStyle().passwordToggle(true)` — 自动在右侧图标处添加显示/隐藏按钮。若开启 `passwordToggle`，字段会默认采用 `PASSWORD`，并通过 `ensurePasswordToggleIcons()` 注入默认的眼睛图标（`` 闭眼 / `` 睁眼）。
- `campo.<TextFieldStyle>getStyle().passwordHiddenIcon(icon)` / `.passwordVisibleIcon(icon)` — 自定义图标（可选）。
- `TextField.isPassword()` — 当模式为 `PASSWORD` 时返回 `true`（用于条件逻辑）。
- `TextField.getText()` — 返回明文密码（掩码只是视觉上的）。

> 切换在 `onTouchUp` 内部处理：点击右侧图标时，模式会在 default 和 focused 样式中于 `PASSWORD` 和 `NORMAL` 之间切换，并更换图标。你不需要注册 `rightIconAction`——如果想要自动行为，请不要覆盖右侧图标。

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

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;   // TextFieldStyle（在样式中配置）

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

      // 简单密码（无显示按钮）
      TextField senha = new TextField("");
      senha.caption("Senha");
      senha.<TextFieldStyle>getStyle().mode(EditMode.PASSWORD);

      // 带显示/隐藏按钮的密码
      TextField senhaToggle = new TextField("");
      senhaToggle.caption("Confirmar senha");
      senhaToggle.<TextFieldStyle>getStyle()
         .mode(EditMode.PASSWORD)
         .passwordToggle(true);   // 自动注入眼睛图标

      Label resultado = new Label("");

      Button entrar = new Button("Entrar");
      entrar.onAction(o ->
      {
         String pwd = senha.getText();          // 真实文本，未掩码
         String pwd2 = senhaToggle.getText();
         if (!pwd.equals(pwd2))
         {
            Hint.show("As senhas não coincidem", 2000);
            return;
         }
         resultado.setText("Senha confirmada (" + pwd.length() + " caracteres)");
      });

      add(LAY_FILL, senha);
      add(LAY_FILL, senhaToggle);
      add(LAY_AUTO, entrar);
      add(LAY_FILL, resultado);
   }
}
```

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

## 最小片段
```java
import dev.crossfly.ui.*;
import dev.crossfly.ui.style.*;
import static dev.crossfly.ui.UIConstants.EditMode;

// 简单密码（无显示按钮）
TextField senha = new TextField("");
senha.caption("Senha");
senha.<TextFieldStyle>getStyle().mode(EditMode.PASSWORD);

// 带显示/隐藏按钮的密码
TextField senhaToggle = new TextField("");
senhaToggle.caption("Senha");
senhaToggle.<TextFieldStyle>getStyle()
   .mode(EditMode.PASSWORD)
   .passwordToggle(true);   // 自动注入眼睛图标

Button entrar = new Button("Entrar");
entrar.onAction(o ->
{
   String pwd = senhaToggle.getText(); // 真实文本，未掩码
   // ... 进行认证
});
```

## 深入了解
- [文本字段](/docs/ui/forms-and-input/text-fields) · [焦点与验证](/docs/ui/forms-and-input/focus-and-validation)
- 姊妹配方：[验证字段](/docs/cookbook/forms/validar-campo) · [响应式表单](/docs/cookbook/forms/formulario-responsivo)
- 权威来源：第 14 章 — 基础控件 · [枚举 (UIConstants)](/docs/reference/enums-uiconstants)
