Pular para o conteúdo
CrossFly é 100% open sourceSiga no GitHub
CrossFlyCrossFly
Começar
Todos os exemplos
ReceitasAvisosListas160 linhas · 1 arquivos

Empty, loading and broken

Three of these are usually left to render as blank space. Blank space reads as a bug, never as an answer.

O que isto mostra

The four states every screen has, and the three nobody designs.

A screen is drawn once, for the case where the data arrived and there is plenty of it. Then it ships, and users meet the other three: nothing yet, nothing matching, and something went wrong. All three render as an empty rectangle unless somebody decided otherwise, and an empty rectangle says only one thing to the person looking at it — this is broken.

The distinction that matters most is the one between the first two. "You have not added anything yet" invites an action and is a good first impression. "Nothing matches that search" is an answer to a question the user asked. Showing the first when the second is true tells a long-standing customer their account is empty.

Rodar na sua máquina

Todo exemplo é uma Window comum, então o launcher só precisa do nome dela.

gradlew lwjgl3:run --args="classname dev.cf.recipe.R30Empty"
Editar o exemplo

Java é compilado antes de rodar, então o navegador não consegue reconstruir uma edição sozinho — o código que você vê aqui virou JavaScript pelo TeaVM antes de ser publicado. Copie o arquivo para um projeto e ele roda sem mudanças nas quatro plataformas; o comando acima é toda a preparação.

Rodando aquiCrossFly 0.1.32
Iniciando o aplicativo…

Compilado para JavaScript pelo TeaVM — é o mesmo código, não um vídeo dele. · Todos os exemplos

O que ele usa

Tipos do framework que este exemplo toca. Os que têm link têm página de referência.

ButtonCardChipComponentDebugGapGridLayoutHintHorizontalLayoutPalColorScreenStylesVerticalLayoutWidgets

O código

Completo, sem cortes. É o arquivo, não um trecho dele.

Bruto
dev/cf/recipe/R30Empty.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.*;

/**
 * The four states every screen has, and the three nobody designs.
 *
 * <p>A screen is drawn once, for the case where the data arrived and there is plenty of it. Then it
 * ships, and users meet the other three: nothing yet, nothing matching, and something went wrong.
 * All three render as an empty rectangle unless somebody decided otherwise, and an empty rectangle
 * says only one thing to the person looking at it — <b>this is broken</b>.</p>
 *
 * <p>The distinction that matters most is the one between the first two. "You have not added
 * anything yet" invites an action and is a good first impression. "Nothing matches that search" is
 * an answer to a question the user asked. Showing the first when the second is true tells a
 * long-standing customer their account is empty.</p>
 */
public class R30Empty extends Recipe
{
   private enum State
   {
      LOADED, FIRST_RUN, NO_MATCH, FAILED
   }

   private State state = State.FIRST_RUN;
   private Component stage;

   public String title()
   {
      return "Empty, loading and broken";
   }

   public String note()
   {
      return "Three of these are usually left to render as blank space. Blank space reads as a bug,"
         + " never as an answer.";
   }

   public void build(Component body) throws Exception
   {
      Component switcher = new Component();
      switcher.setLayout(new HorizontalLayout(Position.START, Position.CENTER));
      for (final State s : State.values())
      {
         Chip c = new Chip(s.name()
            .toLowerCase()
            .replace('_', ' '));
         c.setSelected(s == state);
         c.getStyle()
            .outgap(Gap.ofFont(0, 0, .4f, 0));
         c.onAction(e ->
         {
            state = s;
            rebuild();
            Screen.repaint();
         });
         switcher.add(LAY_AUTO, c);
      }
      body.add(LAY_AUTO, outgap(switcher, 0, 0, 0, 1f));

      stage = new Component();
      stage.setLayout(new VerticalLayout(Position.START, Position.START));
      stage.testTag("recipe.states");
      body.add(LAY_AUTO, stage);
      rebuild();
   }

   private void rebuild()
   {
      try
      {
         stage.removeAll();
         // removeAll() takes the layout with it.
         stage.setLayout(new VerticalLayout(Position.START, Position.START));

         switch (state)
         {
            case LOADED:
               for (int i = 1; i <= 4; i++)
               {
                  Card c = new Card();
                  c.setLayout(new GridLayout(Position.START, Position.CENTER));
                  c.getStyle()
                     .outgap(Gap.ofFont(0, 0, 0, .5f));
                  Component t = new Component();
                  t.setLayout(new VerticalLayout(Position.START, Position.START));
                  t.add(LAY_AUTO, label("Order " + (4180 + i), 1.2f, true, Styles.textColor,
                     Position.START));
                  t.add(LAY_AUTO, label("Placed this week", 1f, false, Styles.captionColor,
                     Position.START));
                  c.add(0f, 1f, Tones.tile(0xE8CC, Tones.cycle(i)), outgap(t, .6f, 0, 0, 0));
                  stage.add(LAY_AUTO, c);
               }
               break;

            case FIRST_RUN:
               // An invitation, with the action right there. This is a first impression, and the
               // one empty state worth spending a sentence on.
               // An invitation is not a problem: it gets the tone of the thing being offered.
               stage.add(LAY_AUTO, state(0xE8CC, Tones.accent(), "No orders yet",
                  "When you place one, it will show up here.", "Place your first order"));
               break;

            case NO_MATCH:
               // An ANSWER to what the user typed, not an invitation. Offering "add your first
               // order" to somebody who searched for one they know exists reads as being ignored.
               // Nothing is broken — the search simply has no answer. Not red.
               stage.add(LAY_AUTO, state(0xE8B6, Tones.support(), "Nothing matches “ironvale”",
                  "Check the spelling, or clear the search to see everything.",
                  "Clear the search"));
               break;

            case FAILED:
               // What happened, and what they can do. Not the exception text: it names something
               // the user has no way to act on, and it frightens them.
               // The only one of the three that actually failed, and the only one in the error
               // tone. Three identical grey glyphs said all three were the same event.
               stage.add(LAY_AUTO, state(0xE000, Tones.danger(), "We could not load your orders",
                  "Nothing was lost. Check your connection and try again.", "Try again"));
               break;

            default:
               break;
         }
         stage.setValidLayout(false);
      }
      catch (Exception e)
      {
         dev.crossfly.util.Debug.handle(e);
      }
   }

   /** One shape for all three: a glyph, a sentence, a reason, and exactly one way forward. */
   private Component state(int glyph, PalColor tom, String title, String detail, String action)
      throws Exception
   {
      Component c = new Component();
      c.setLayout(new VerticalLayout(Position.CENTER, Position.CENTER));
      c.getStyle()
         .ingap(Gap.ofFont(1f, 2f, 1f, 2f));
      c.add(LAY_AUTO, outgap(matglyph(glyph, tom, 3f), 0, 0, 0, .8f));
      c.add(LAY_AUTO, outgap(label(title, 1.5f, true, Styles.textColor, Position.CENTER), 0, 0, 0,
         .3f));
      c.add(LAY_AUTO, outgap(label(detail, 1f, false, Styles.captionColor, Position.CENTER), 0, 0,
         0, 1f));
      Button b = Tones.action(action, tom);
      b.onAction(e -> Hint.show(action, 1500));
      c.add(LAY_AUTO, b);
      return c;
   }
}
Para uma IA

Versões desta página legíveis por máquina, para um modelo ler a prateleira em vez de adivinhar a API.

Este exemplo, em JSONEste exemplo, em Markdown
AnteriorBrand and dark modePróximoFiles, sharing and printing