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

Brand and dark mode

One seed generates the whole palette, for both themes. Nothing below names a colour, which is why the switch needs no rebuild.

O que isto mostra

Declaring a brand: one seed, and everything else follows.

You do not pick colours in a CrossFly app. You declare one seed, and the framework derives the rest — primary and what reads on it, surfaces, containers, outlines, the tonal fill behind an active tab, both themes. Tap the swatches below: nothing on this screen is rebuilt, and every colour moves at once, because none of them was ever written down.

Skip this and your app is not "unstyled", it is grey — the skin's neutral palette is what you get when nobody declared anything, and it reads as a rendering bug rather than as a default. That is the first thing to check when a screen looks broken and nothing is broken.

Two calls, and the difference between them matters exactly once. setBrand restyles every open window; setBrandSilent does not, and exists for code running before there is anything to restyle — class initialisation, the line before the first window is shown.

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

ButtonCardColorSchemeComponentGapHorizontalLayoutStylesSwitchTextFieldWidgets

O código

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

Bruto
dev/cf/recipe/R29Brand.java
package dev.cf.recipe;

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

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

/**
 * Declaring a brand: one seed, and everything else follows.
 *
 * <p>You do not pick colours in a CrossFly app. You declare one seed, and the framework derives the
 * rest — primary and what reads on it, surfaces, containers, outlines, the tonal fill behind an
 * active tab, both themes. Tap the swatches below: nothing on this screen is rebuilt, and every
 * colour moves at once, because none of them was ever written down.</p>
 *
 * <p><b>Skip this and your app is not "unstyled", it is grey</b> — the skin's neutral palette is
 * what you get when nobody declared anything, and it reads as a rendering bug rather than as a
 * default. That is the first thing to check when a screen looks broken and nothing is broken.</p>
 *
 * <p>Two calls, and the difference between them matters exactly once. {@code setBrand} restyles
 * every open window; {@code setBrandSilent} does not, and exists for code running before there is
 * anything to restyle — class initialisation, the line before the first window is shown.</p>
 */
public class R29Brand extends Recipe
{
   /** A few seeds. Any ARGB works: the palette is generated, not looked up. */
   private static final int[] SEED = { 0xFF1565C0, 0xFFE2622C, 0xFF2E7D32, 0xFF6A1B9A,
      0xFFB71C1C };
   private static final String[] SEED_NAME = { "Blue", "Orange", "Green", "Purple", "Red" };

   /** What was in force when this recipe opened, so it can be handed back. */
   private Integer previousBrand;
   private ColorScheme previousScheme;

   public String title()
   {
      return "Brand and dark mode";
   }

   public String note()
   {
      return "One seed generates the whole palette, for both themes. Nothing below names a colour,"
         + " which is why the switch needs no rebuild.";
   }

   public void build(Component body) throws Exception
   {
      remember();

      body.add(LAY_AUTO, outgap(label("THE SEED", 1f, true, Styles.captionColor, Position.START), 0,
         0, 0, .5f));

      Component swatches = new Component();
      swatches.setLayout(new HorizontalLayout(Position.START, Position.CENTER));
      for (int i = 0; i < SEED.length; i++)
      {
         final int seed = SEED[i];
         Button b = new Button(SEED_NAME[i]);
         b.getStyle()
            .backColor(seed)
            .textColor(0xFFFFFFFF)
            .outgap(Gap.ofFont(0, 0, .4f, 0));
         b.onAction(c -> Styles.setBrand(seed));
         swatches.add(LAY_AUTO, b);
      }
      body.add(LAY_AUTO, outgap(swatches, 0, 0, 0, 1f));

      Switch dark = new Switch("Dark mode");
      dark.checked(Styles.isDarkMode());
      // One call. Every PalColor token re-resolves in place and every window is restyled — no
      // screen tracks a flag of its own, and none rebuilds.
      dark.onAction(c -> Styles.setDarkMode(dark.isChecked()));
      body.add(LAY_AUTO, outgap(dark, 0, 0, 0, 1.2f));

      body.add(LAY_AUTO, outgap(label("WHAT FOLLOWS, WITHOUT BEING TOLD", 1f, true,
         Styles.captionColor, Position.START), 0, 0, 0, .5f));

      Card c = new Card();
      c.add(LAY_AUTO, label("A card on a surface", 1.2f, true, Styles.textColor, Position.START));
      c.add(LAY_AUTO, outgap(label("Caption text, the tone below the title.", 1f, false,
         Styles.captionColor, Position.START), 0, .2f, 0, .5f));
      c.add(LAY_AUTO, outgap(new TextField().caption("A field"), 0, 0, 0, .5f));
      c.add(LAY_AUTO, outgap(new Button("A button"), 0, 0, 0, .5f));
      Component chips = new Component();
      chips.setLayout(new HorizontalLayout(Position.START, Position.CENTER));
      chips.add(LAY_AUTO, outgap(toneBadge(Styles.primaryColor, "primary"), 0, 0, .4f, 0));
      chips.add(LAY_AUTO, outgap(toneBadge(secondary(), "secondary"), 0, 0, .4f, 0));
      chips.add(LAY_AUTO, toneBadge(tertiary(), "tertiary"));
      c.add(LAY_AUTO, chips);
      body.add(LAY_AUTO, c);
   }

   /**
    * Snapshots the palette, so leaving this recipe does not repaint the app it was opened from.
    *
    * <p>A <b>copy</b>, never the reference: {@link Styles} keeps one {@link ColorScheme} instance
    * for the life of the app and mutates its roles in place on every seed change — deliberately, so
    * that a {@code PalColor} captured into a style at build time keeps resolving. Save the
    * reference and you have saved the object that is about to be overwritten.</p>
    */
   private void remember()
   {
      previousBrand = Styles.hasBrand() ? Integer.valueOf(Styles.brand()) : null;
      ColorScheme live = Styles.colorScheme();
      if (live != null)
      {
         previousScheme = ColorScheme.fromSeed(SEED[0], live.isDark);
         previousScheme.copyFrom(live);
      }
   }

   @Override
   public void onRemove()
   {
      super.onRemove();
      if (previousBrand != null)
         Styles.setBrand(previousBrand.intValue());
      else
      {
         Styles.clearBrand();
         if (previousScheme != null)
            Styles.setColorScheme(previousScheme);
      }
   }
}
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
AnteriorWriting your own componentPróximoEmpty, loading and broken