Saltar al contenido
CrossFly es 100% open sourceSíguenos en GitHub
CrossFlyCrossFly
Empezar
Todos los ejemplos
RecetasEntrada84 líneas · 1 archivos

Pickers

show(answer -> …) on all of them, and answer is null when the user backed out. The ones needing hardware live in R21, and the system file picker in R31 — those can be refused.

Qué muestra esto

The pickers, and the callback shape they all share.

Every picker is a Window that answers: show(answer -> …), where answer is null when the user cancelled. One shape for date, time, colour, file, photo, barcode and location — so learning one is learning all of them.

Handle the null. Cancelling is not an error and not an edge case; it is the most common outcome of opening a picker by mistake, and a callback that assumes an answer throws on a path every user takes.

For a date inside a form, do not open a picker by hand at all — a TextField in EditMode.DATE opens it for you and writes the answer back. See R10Form.

Ejecutarlo en tu máquina

Cada ejemplo es una Window normal, así que el lanzador solo necesita su nombre.

gradlew lwjgl3:run --args="classname dev.cf.recipe.R20Pickers"
Editarlo

Java se compila antes de ejecutarse, así que el navegador no puede reconstruir una edición por su cuenta: el código que ves aquí se convirtió en JavaScript con TeaVM antes de publicarse. Copia el archivo a un proyecto y funciona sin cambios en las cuatro plataformas; el comando de arriba es toda la preparación.

Ejecutándose aquíCrossFly 0.1.32
Iniciando la aplicación…

Compilado a JavaScript por TeaVM: es el mismo código, no un vídeo de él. · Todos los ejemplos

Qué usa

Tipos del framework que toca este ejemplo. Los enlazados tienen página de referencia.

CardColorPickerComponentDatePickerDateRangePickerGapGridLayoutLabelScreenStylesTimePickerWidgets

El código

Completo, sin recortes. Es el archivo, no un fragmento.

Crudo
dev/cf/recipe/R20Pickers.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.picker.*;
import dev.crossfly.ui.style.*;

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

/**
 * The pickers, and the callback shape they all share.
 *
 * <p>Every picker is a {@link dev.crossfly.ui.Window} that answers:
 * {@code show(answer -> …)}, where {@code answer} is {@code null} when the user cancelled. One
 * shape for date, time, colour, file, photo, barcode and location — so learning one is learning
 * all of them.</p>
 *
 * <p><b>Handle the null.</b> Cancelling is not an error and not an edge case; it is the most common
 * outcome of opening a picker by mistake, and a callback that assumes an answer throws on a path
 * every user takes.</p>
 *
 * <p>For a date <i>inside a form</i>, do not open a picker by hand at all — a {@code TextField} in
 * {@code EditMode.DATE} opens it for you and writes the answer back. See {@link R10Form}.</p>
 */
public class R20Pickers extends Recipe
{
   private Label answer;

   public String title()
   {
      return "Pickers";
   }

   public String note()
   {
      return "show(answer -> …) on all of them, and answer is null when the user backed out. The"
         + " ones needing hardware live in R21, and the system file picker in R31 — those can be"
         + " refused.";
   }

   public void build(Component body) throws Exception
   {
      answer = label("Nothing picked yet.", 1f, false, Styles.captionColor, Position.START);
      answer.testTag("pickers.answer");
      // Pinned below the scrolling body, so the answer stays in view (see Recipe.pin).
      pin(answer);

      open(body, 0xE878, "Date", () -> new DatePicker().show(a -> said("Date", a)));
      open(body, 0xE8B5, "Time", () -> new TimePicker().show(a -> said("Time", a)));
      open(body, 0xE916, "Date range", () -> new DateRangePicker().show(a -> said("Range", a)));
      open(body, 0xE40A, "Colour", () -> new ColorPicker(Styles.primaryColor.color())
         .show(a -> said("Colour", a)));
   }

   /** A row per picker: its glyph on a tonal tile, its name, and the whole card opens it. */
   private void open(Component body, int icone, String name, Runnable go) throws Exception
   {
      Card c = new Card();
      c.setLayout(new GridLayout(Position.START, Position.CENTER));
      c.getStyle()
         .outgap(Gap.ofFont(0, 0, 0, .5f))
         .pressable();
      c.testTag("pickers." + name.toLowerCase()
         .replace(' ', '.'));
      c.onAction(e -> go.run());
      c.add(0f, 1f, 0f, Tones.tile(icone, Tones.accent()),
         outgap(label(name, 1.2f, true, Styles.textColor, Position.START), .6f, 0, 0, 0),
         matglyph(0xE5CC, tertiary(), 1f));   // chevron_right
      body.add(LAY_AUTO, c);
   }

   /** One place that handles the answer, including the null every picker can return. */
   private void said(String what, Object a)
   {
      answer.setText(a == null ? what + ": cancelled" : what + ": " + a);
      answer.invalidateMeasure(); // the pinned band must grow with it (see Recipe.pin)
      Screen.repaint();
   }
}
Para una IA

Versiones de esta página legibles por máquina, para que un modelo lea el estante en vez de adivinar la API.

Este ejemplo, en JSONEste ejemplo, en Markdown
AnteriorCapturing a signatureSiguienteCamera, barcode and GPS