Skip to content
CrossFly is 100% open sourceFollow us on GitHub
CrossFlyCrossFly
Get started
All examples
RecipesLists113 lines · 1 files

Listing: cards or table

A card answers "what is this one?"; a row answers "how do these compare?". Which one the reader needs changes during the day, so Listing lets them switch.

What this shows

The same items as cards or as a table, with the reader choosing — and a frozen first column.

Run it on your machine

Every example is a plain Window, so the launcher only needs its name.

gradlew lwjgl3:run --args="classname dev.cf.recipe.R05Listing"
Editing it

Java is compiled ahead of time, so the browser cannot rebuild an edit on its own — the code you see here was turned into JavaScript by TeaVM before it was published. Copy the file into a project and it runs unchanged on all four platforms; the command above is the whole setup.

Running hereCrossFly 0.1.32
Starting the app…

Compiled to WebAssembly-era JavaScript by TeaVM — the same code, not a video of it. · All examples

What it uses

Framework types this example touches. The linked ones have a reference page.

CardComponentDataGridGridLayoutListingStylesVerticalLayoutWidgets

The source

Complete and unabridged. This is the file, not an excerpt of it.

Raw
dev/cf/recipe/R05Listing.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 same items as cards or as a table, with the reader choosing — and a frozen first column.
 */
public class R05Listing extends Recipe
{
   // Brazilian company and city names, kept as sample data: proper names of a Brazilian client list
   // (iniciais() below also relies on "Casa do Norte" having a preposition in the middle).
   private static final String[] NOME ={ "Aurora Distribuidora", "Primavera Norte", "Vale Sul",
      "Casa do Norte", "Mercantil Boa Vista", "Atacado Serra", "Comercial Ipê" };
   private static final String[] CIDADE = { "Campinas", "Sumaré", "Indaiatuba", "Jundiaí",
      "Valinhos", "Vinhedo", "Itu" };
   private static final int[] VALOR = { 12400, 8300, 21750, 4120, 15990, 6480, 9310 };

   public String title()
   {
      return "Listing: cards or table";
   }

   public String note()
   {
      return "A card answers \"what is this one?\"; a row answers \"how do these compare?\". Which"
         + " one the reader needs changes during the day, so Listing lets them switch.";
   }

   public void build(Component body) throws Exception
   {
      Listing lista = new Listing().cardWidth(20f)
         .columns(1, 3)
         .column("nome", "Client", 13f)
         .column("cidade", "City", 9f)
         .column("valor", "Amount", 8f, Position.END)
         // The frozen column does not leave the view when the table scrolls horizontally — it is
         // what tells whose row you are reading out at the far right.
         .freeze(Position.START, "nome")
         .items(new Listing.Items()
         {
            public int count()
            {
               return NOME.length;
            }

            public Component card(int i) throws Exception
            {
               // Tile on the left, the three lines on the right. A column of name-over-city-over-
               // value is three greys with nothing for the eye to land on between rows; the tile
               // is one line of code and it is what makes the list scannable.
               Card c = new Card();
               c.setLayout(new GridLayout(Position.START, Position.CENTER));

               Component texto = new Component();
               texto.setLayout(new VerticalLayout(Position.START, Position.START));
               texto.add(LAY_AUTO, label(NOME[i], 1.2f, true, Styles.textColor, Position.START));
               texto.add(LAY_AUTO, label(CIDADE[i], 1f, false, Styles.captionColor,
                  Position.START));
               texto.add(LAY_AUTO, outgap(label(valor(i), 1.1f, true, Tones.accent(),
                  Position.START), 0, .15f, 0, 0));

               c.add(LAY_AUTO, 1f, Tones.tile(iniciais(i), Tones.cycle(i)),
                  outgap(texto, .6f, 0, 0, 0));
               return c;
            }

            public void row(DataGrid.Row r, int i) throws Exception
            {
               r.cell(0, label(NOME[i], 1f, true, Styles.textColor, Position.START));
               r.cell(1, label(CIDADE[i], 1f, false, Styles.captionColor, Position.START));
               // The amount is what the table exists to compare, so it is the one coloured cell.
               r.cell(2, label(valor(i), 1f, true, Tones.accent(), Position.END));
            }
         });
      lista.testTag("recipe.listing");

      // The switch has to be placed by the SCREEN. Listing offers modeButton() and never draws it
      // itself, because a listing usually sits under a toolbar that already has a count and a sort
      // control, and a bar of its own would be a second one. Leave this line out and the recipe
      // shows cards with no way to reach the table — which is what it did until somebody looked.
      Component barra = new Component();
      barra.setLayout(new GridLayout(Position.START, Position.CENTER));
      barra.add(1f, 0f, label("7 clients", 1f, false, Styles.captionColor, Position.START),
         lista.modeButton());
      body.add(LAY_AUTO, outgap(barra, 0, 0, 0, .5f));

      body.add(LAY_FILL, lista);
   }

   /** The first letter of each of the first two words — "Aurora Distribuidora" becomes AD. */
   private static String iniciais(int i)
   {
      String[] p = NOME[i].split(" ");
      // Upper case both, or "Casa do Norte" answers "Cd" — the second word is a preposition and
      // the tile ends up shouting the one letter that carries no information.
      return p.length > 1 ? ("" + p[0].charAt(0) + p[p.length - 1].charAt(0)).toUpperCase()
         : p[0].substring(0, 2).toUpperCase();
   }

   private static String valor(int i)
   {
      return "R$ " + VALOR[i] / 100 + "," + (VALOR[i] % 100 < 10 ? "0" : "") + VALOR[i] % 100;
   }
}
For an AI assistant

Machine-readable versions of this page, so a model can read the shelf instead of guessing at the API.

This example, as JSONThis example, as Markdown
PreviousBottom sheet with a fixed footerNextChoosing from a list