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

Switching skins

Shape, radius, padding and typeface all come from the skin. Nothing below knows which one is in force.

What this shows

The same screen under Material 3, Fluent and iOS — with no per-skin code anywhere.

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

ButtonCardComponentDebugFluentSkinHorizontalLayoutIOSSkinMaterialSkinScreenSkinStylesSwitchTextFieldWidgets

The source

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

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

import dev.crossfly.ui.*;
import dev.crossfly.ui.graphics.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.skin.*;
import dev.crossfly.ui.style.*;

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

/**
 * The same screen under Material 3, Fluent and iOS — with no per-skin code anywhere.
 */
public class R03Skins extends Recipe
{
   public String title()
   {
      return "Switching skins";
   }

   public String note()
   {
      return "Shape, radius, padding and typeface all come from the skin. Nothing below knows which"
         + " one is in force.";
   }

   public void build(Component body) throws Exception
   {
      Component linha = new Component();
      linha.setLayout(new HorizontalLayout(Position.START, Position.CENTER));
      // One per call: the add with several weights belongs to GridLayout (see R01Layouts).
      linha.add(LAY_AUTO, outgap(botao("Material", new MaterialSkin()), 0, 0, .5f, 0));
      linha.add(LAY_AUTO, outgap(botao("Fluent", new FluentSkin()), 0, 0, .5f, 0));
      linha.add(LAY_AUTO, botao("iOS", new IOSSkin()));
      body.add(LAY_AUTO, outgap(linha, 0, 0, 0, 1f));

      // Pieces whose shape comes entirely from the skin — that is where the switch shows.
      body.add(LAY_AUTO, outgap(new TextField().caption("A field"), 0, 0, 0, .6f));
      body.add(LAY_AUTO, outgap(new Button("A button"), 0, 0, 0, .6f));
      body.add(LAY_AUTO, outgap(new Switch("A switch"), 0, 0, 0, .6f));
      Card cartao = new Card();
      cartao.add(LAY_AUTO, label("A card", 1.2f, true, Styles.textColor, Position.START));
      body.add(LAY_AUTO, cartao);
   }

   /**
    * {@code apply(skin, onApplied)}, never {@code apply(skin)}.
    *
    * <p>On the web the font atlas of a skin the app did not start with is downloaded on demand, and
    * only the callback form waits for it. On native builds the callback runs before the method
    * returns, so this one line is right on every platform.</p>
    */
   private Button botao(String rotulo, Skin skin)
   {
      Button b = new Button(rotulo);
      b.onAction(c -> Skin.apply(skin, this::recriar));
      return b;
   }

   /** Switching skins is the app's decision, so the rebuild is here — never inside the rebuild itself. */
   private void recriar()
   {
      try
      {
         removeAll();
         onAdd(false);
         setValidLayout(false);
         Screen.repaint();
      }
      catch (Exception e)
      {
         dev.crossfly.util.Debug.handle(e);
      }
   }
}
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
PreviousTheme tokensNextBottom sheet with a fixed footer