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

Charts

autoThemeColors themes the axes, grid and background — but NOT the series. A series left at colour 0 falls back to a library grey, and becomes the one thing on screen ignoring your brand.

What this shows

Four charts, and the one line that stops them looking foreign.

Which chart is a question about the reader, not the data. Over time is a line; compared side by side is bars; share of a whole is a donut; a trend inside a card is a mini bar chart with no axes at all, because axes on a two-centimetre chart are decoration.

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.R16Charts"
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.

CardChartViewColorUtilsComboChartComponentDonutChartLineChartMiniBarChartPalColorRelativeStylesWidgets

The source

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

Raw
dev/cf/recipe/R16Charts.java
package dev.cf.recipe;

import dev.cf.Tones;

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

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

/**
 * Four charts, and the one line that stops them looking foreign.
 *
 * <p>Which chart is a question about the reader, not the data. <b>Over time</b> is a line;
 * <b>compared side by side</b> is bars; <b>share of a whole</b> is a donut; <b>a trend inside a
 * card</b> is a mini bar chart with no axes at all, because axes on a two-centimetre chart are
 * decoration.</p>
 */
public class R16Charts extends Recipe
{
   private static final String[] MONTH = { "Jan", "Feb", "Mar", "Apr", "May", "Jun" };
   private static final double[] REVENUE = { 4200, 5100, 4800, 6300, 7100, 6800 };
   private static final double[] ORDERS = { 31, 38, 35, 46, 52, 49 };

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

   public String note()
   {
      return "autoThemeColors themes the axes, grid and background — but NOT the series. A series"
         + " left at colour 0 falls back to a library grey, and becomes the one thing on screen"
         + " ignoring your brand.";
   }

   public void build(Component body) throws Exception
   {
      int brand = Styles.primaryColor.color();
      // The second series is a ROLE, not the brand dimmed towards the text colour. Two measures on
      // one chart have to be told apart at a glance, and the old shade came out grey-blue — the
      // reader had to find the legend to know which line was which. Tertiary sits sixty degrees
      // off the seed and is derived from it, so it is distinct without being unrelated.
      int shade = Tones.highlight().color();

      head(body, 0xE8E5, "Over time", Tones.accent());
      LineChart line = new LineChart();
      line.categories(MONTH);
      line.series(new ChartView.Series("Revenue", REVENUE, brand));
      line.area(true);
      line.smooth(true);
      chart(body, line);

      head(body, 0xE26B, "Side by side", Tones.accent());
      ComboChart combo = new ComboChart();
      combo.categories(MONTH);
      combo.addSeries(new ChartView.Series("Revenue", REVENUE, brand).bar());
      combo.addSeries(new ChartView.Series("Orders", ORDERS, shade).line());
      chart(body, combo);

      head(body, 0xE6C4, "Share of a whole", Tones.accent());
      DonutChart donut = new DonutChart();
      // One hue in steps, not four unrelated colours: the question is "how much of it", and a
      // legend the reader has to learn is a worse answer than a ramp they can just see.
      String[] channel = { "Field", "Web", "Phone", "Partner" };
      double[] share = { 46, 28, 17, 9 };
      for (int i = 0; i < channel.length; i++)
         donut.slice(channel[i], share[i],
            ColorUtils.lerpColor(brand, Styles.backColor.color(), i * .21f));
      donut.hole(.62f);
      chart(body, donut);

      head(body, 0xE873, "Inside a card", Tones.accent());
      Card c = new Card();
      c.add(LAY_AUTO, label("Last six months", 1f, true, Tones.accent(), Position.START));
      MiniBarChart mini = new MiniBarChart().values(REVENUE)
         .categories(MONTH)
         .highlight(REVENUE.length - 1);
      mini.getStyle()
         .autoHeight(Relative.ofFont(5f));
      c.add(LAY_AUTO, outgap(mini, 0, .4f, 0, 0));
      body.add(LAY_AUTO, c);
   }

   private void chart(Component body, ChartView v) throws Exception
   {
      v.autoThemeColors(true);
      v.getStyle()
         .autoHeight(Relative.ofFont(12f));
      body.add(LAY_AUTO, outgap(v, 0, 0, 0, 1.2f));
   }

   private void head(Component body, int icone, String t, PalColor tom) throws Exception
   {
      body.add(LAY_AUTO, Tones.head(icone, t, tom));
   }
}
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
PreviousA header that collapsesNextA list too big to build