跳到内容
CrossFly 是 100% 开源在 GitHub 关注我们
CrossFlyCrossFly
开始使用
全部示例
配方输入110 行 · 1 个文件

Keyboard and focus

Tab follows the order you ADDED the fields, not where they ended up on screen. Build in reading order and the tab order is correct without being designed.

它展示了什么

Filling a form without touching the screen.

Every CrossFly app runs on the desktop, and on the desktop a form is filled with the keyboard. Tab walks the fields in the order they were added, which is why building a form in reading order is not only tidier — it is the tab order, for free. Build it out of order and the user tabs from the postcode back up to the surname.

Two style flags decide who can be reached. focusOnTouch means tapping this gives it the keyboard; focusOnKey means a keypress can move focus into it. A button that should be reachable by Tab needs the second, and a display-only panel wants neither — a Tab stop on something that does nothing is a Tab stop the user has to press through every time.

The other keyboard is the one you bring with you: NumericKeypad is an on-screen pad for a device that has no keys at all, or for a value that is only ever digits — a warehouse terminal, a scale, a till.

在你的机器上运行

每个示例都是普通的 Window,启动器只需要它的名字。

gradlew lwjgl3:run --args="classname dev.cf.recipe.R27Focus"
修改它

Java 是提前编译的,浏览器无法自行重建你的修改 —— 这里的代码在发布前已由 TeaVM 转成 JavaScript。把文件复制到项目里,它在四个平台上原样运行;上面那条命令就是全部准备工作。

正在此处运行CrossFly 0.1.32
正在启动应用…

由 TeaVM 编译为 JavaScript —— 是同一份代码,不是录像。 · 全部示例

它用到了什么

本示例触及的框架类型。带链接的有参考页。

ButtonComponentHintLabelNumericKeypadScreenStylesTextFieldWidgets

源码

完整无删减。这就是那个文件,不是片段。

原始
dev/cf/recipe/R27Focus.java
package dev.cf.recipe;

import dev.cf.Tones;

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

import static dev.crossfly.ui.Widgets.*;

/**
 * Filling a form without touching the screen.
 *
 * <p>Every CrossFly app runs on the desktop, and on the desktop a form is filled with the keyboard.
 * Tab walks the fields in the order they were <b>added</b>, which is why building a form in reading
 * order is not only tidier — it is the tab order, for free. Build it out of order and the user
 * tabs from the postcode back up to the surname.</p>
 *
 * <p>Two style flags decide who can be reached. {@code focusOnTouch} means tapping this gives it
 * the keyboard; {@code focusOnKey} means a keypress can move focus into it. A button that should
 * be reachable by Tab needs the second, and a display-only panel wants neither — a Tab stop on
 * something that does nothing is a Tab stop the user has to press through every time.</p>
 *
 * <p>The other keyboard is the one you bring with you: {@link NumericKeypad} is an on-screen pad
 * for a device that has no keys at all, or for a value that is only ever digits — a warehouse
 * terminal, a scale, a till.</p>
 */
public class R27Focus extends Recipe
{
   private Label reached;

   public String title()
   {
      return "Keyboard and focus";
   }

   public String note()
   {
      return "Tab follows the order you ADDED the fields, not where they ended up on screen. Build"
         + " in reading order and the tab order is correct without being designed.";
   }

   public void build(Component body) throws Exception
   {
      body.add(LAY_AUTO, outgap(label("Press Tab. It walks these in the order they were added.", 1f,
         false, Styles.captionColor, Position.START), 0, 0, 0, .8f));

      field(body, "Name");
      field(body, "Company");
      field(body, "Postcode");

      reached = label("Focus has not reached the button yet.", 1f, false, Styles.captionColor,
         Position.START);
      body.add(LAY_AUTO, outgap(reached, 0, .4f, 0, .6f));

      Button send = Tones.action("Send");
      // Reachable by keyboard, not only by pointer. Without this the last thing in the form is the
      // one thing a keyboard user cannot get to, and they have to reach for the mouse to finish.
      send.getStyle()
         .focusOnKey(true)
         .focusOnTouch(true);
      send.onAction(c -> Hint.show("Sent", 1500));
      send.testTag("focus.send");
      body.add(LAY_AUTO, outgap(send, 0, 0, 0, 1.4f));

      body.add(LAY_AUTO, Tones.head(0xE312, "A keypad of your own", Tones.highlight()));
      body.add(LAY_AUTO, outgap(label("For a device with no keyboard, or a value that is only ever"
         + " digits.", 1f, false, Styles.captionColor, Position.START), 0, 0, 0, .6f));

      final TextField total = new TextField();
      total.caption("Total");
      total.setMode(EditMode.CURRENCY);
      total.testTag("focus.total");
      body.add(LAY_AUTO, outgap(total, 0, 0, 0, .6f));

      // The keypad reports TOKENS, not characters — "0".."9", "," and BACK — so the screen decides
      // what each one means. That is what lets the same pad drive a currency field, a quantity and
      // a PIN without the pad knowing about any of them.
      NumericKeypad pad = new NumericKeypad(token ->
      {
         String now = total.getText();
         if (NumericKeypad.BACK.equals(token))
            total.setText(now.isEmpty() ? "" : now.substring(0, now.length() - 1));
         else
            total.setText(now + token);
         Screen.repaint();
      });
      pad.testTag("focus.keypad");
      body.add(LAY_AUTO, pad);
   }

   private void field(Component body, String caption) throws Exception
   {
      TextField f = new TextField();
      f.caption(caption);
      f.testTag("focus." + caption.toLowerCase());
      // Fired when the field takes the keyboard. On a phone this is where a screen scrolls the
      // field clear of the on-screen keyboard, which is what Device.shiftScreen is for.
      f.onFocus(c ->
      {
         reached.setText("Focus is on: " + caption);
         reached.setValidLayout(false);
         Screen.repaint();
      });
      body.add(LAY_AUTO, outgap(f, 0, 0, 0, .6f));
   }
}
给 AI 助手

本页的机器可读版本,让模型直接读取示例集,而不是猜测 API。

本示例(JSON)本示例(Markdown)
上一个Images下一个Writing your own component