diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/api/RadianceSkin.java b/theming/src/main/java/org/pushingpixels/radiance/theming/api/RadianceSkin.java index ac5a5a89f..a168b745e 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/api/RadianceSkin.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/api/RadianceSkin.java @@ -422,7 +422,8 @@ public final ContainerRenderColorTokens getColorRenderTokens(Component comp, throw new IllegalStateException("Color scheme shouldn't be null here. Please " + "report this issue"); } - return componentState.isActive() ? registered.getPrimaryContainerTokens() + + return componentState.isActive() ? registered.getStateRenderTokens(componentState) : registered.getMutedContainerTokens(); } } @@ -432,7 +433,9 @@ public final ContainerRenderColorTokens getColorRenderTokens(Component comp, if (registered == null) { throw new IllegalStateException("Color scheme shouldn't be null here. Please report " + "this issue"); } - return componentState.isActive() ? registered.getPrimaryContainerTokens() + + + return componentState.isActive() ? registered.getStateRenderTokens(componentState) : registered.getMutedContainerTokens(); } diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/api/palette/ColorSchemeUtils.java b/theming/src/main/java/org/pushingpixels/radiance/theming/api/palette/ColorSchemeUtils.java index 3022b3ee7..e4fb7550a 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/api/palette/ColorSchemeUtils.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/api/palette/ColorSchemeUtils.java @@ -30,8 +30,10 @@ package org.pushingpixels.radiance.theming.api.palette; import org.pushingpixels.radiance.theming.api.ComponentState; +import org.pushingpixels.radiance.theming.internal.utils.RadianceColorUtilities; import java.awt.*; +import java.util.HashMap; public class ColorSchemeUtils { public static RadianceColorScheme2 getLightColorScheme(Palettes palettes) { @@ -295,6 +297,8 @@ public Color getContainerOutlineVariant() { }; return new RadianceColorScheme2() { + private HashMap stateTokens = new HashMap<>(); + @Override public Color getSurface() { return paletteColorResolver.getSurface(palettes); @@ -332,7 +336,71 @@ public ContainerRenderColorTokens getPrimaryContainerTokens() { @Override public ContainerRenderColorTokens getStateRenderTokens(ComponentState componentState) { - return null; + // TODO: TONAL - configurable at the skin definition level + ContainerRenderColorTokens defaultActive = this.getPrimaryContainerTokens(); + // TODO: TONAL - configurable at the component state level + int mixinAmount = 40; + if (componentState == ComponentState.PRESSED_UNSELECTED) { + // surface dim on top of muted + if (!stateTokens.containsKey(componentState)) { + stateTokens.put(componentState, + ColorSchemeUtils.overlay( + getMutedContainerTokens(), + getSurfaceDim(), + mixinAmount)); + } + return stateTokens.get(componentState); + } + if (componentState == ComponentState.PRESSED_SELECTED) { + // surface dim on top of active + if (!stateTokens.containsKey(componentState)) { + stateTokens.put(componentState, + ColorSchemeUtils.overlay( + defaultActive, + getSurfaceDim(), + mixinAmount)); + } + return stateTokens.get(componentState); + } + if (componentState == ComponentState.SELECTED) { + return defaultActive; + } + if (componentState == ComponentState.ROLLOVER_UNSELECTED) { + // surface on top of muted + if (!stateTokens.containsKey(componentState)) { + stateTokens.put(componentState, + ColorSchemeUtils.overlay( + getMutedContainerTokens(), + getSurface(), + mixinAmount)); + } + return stateTokens.get(componentState); + } + if (componentState == ComponentState.ROLLOVER_SELECTED) { + // surface bright on top of active + if (!stateTokens.containsKey(componentState)) { + stateTokens.put(componentState, + ColorSchemeUtils.overlay( + defaultActive, + getSurfaceBright(), + mixinAmount)); + } + return stateTokens.get(componentState); + } + + ComponentState hardFallback = componentState.getHardFallback(); + if (hardFallback != null) { + return this.getStateRenderTokens(hardFallback); + } + + if (componentState == ComponentState.ENABLED) { + return getMutedContainerTokens(); + } + if (componentState.isDisabled()) { + // TODO: TONAL - revisit + return getMutedContainerTokens(); + } + return defaultActive; } }; } @@ -598,6 +666,8 @@ public Color getContainerOutlineVariant() { }; return new RadianceColorScheme2() { + private HashMap stateTokens = new HashMap<>(); + @Override public Color getSurface() { return paletteColorResolver.getSurface(palettes); @@ -635,7 +705,156 @@ public ContainerRenderColorTokens getPrimaryContainerTokens() { @Override public ContainerRenderColorTokens getStateRenderTokens(ComponentState componentState) { - return null; + // TODO: TONAL - configurable at the skin definition level + ContainerRenderColorTokens defaultActive = this.getPrimaryContainerTokens(); + // TODO: TONAL - configurable at the component state level + int mixinAmount = 40; + if (componentState == ComponentState.PRESSED_UNSELECTED) { + // surface dim on top of muted + if (!stateTokens.containsKey(componentState)) { + stateTokens.put(componentState, + ColorSchemeUtils.overlay( + getMutedContainerTokens(), + getSurfaceDim(), + mixinAmount)); + } + return stateTokens.get(componentState); + } + if (componentState == ComponentState.PRESSED_SELECTED) { + // surface dim on top of active + if (!stateTokens.containsKey(componentState)) { + stateTokens.put(componentState, + ColorSchemeUtils.overlay( + defaultActive, + getSurfaceDim(), + mixinAmount)); + } + return stateTokens.get(componentState); + } + if (componentState == ComponentState.SELECTED) { + return defaultActive; + } + if (componentState == ComponentState.ROLLOVER_UNSELECTED) { + // surface on top of muted + if (!stateTokens.containsKey(componentState)) { + stateTokens.put(componentState, + ColorSchemeUtils.overlay( + getMutedContainerTokens(), + getSurface(), + mixinAmount)); + } + return stateTokens.get(componentState); + } + if (componentState == ComponentState.ROLLOVER_SELECTED) { + // surface bright on top of active + if (!stateTokens.containsKey(componentState)) { + stateTokens.put(componentState, + ColorSchemeUtils.overlay( + defaultActive, + getSurfaceBright(), + mixinAmount)); + } + return stateTokens.get(componentState); + } + + ComponentState hardFallback = componentState.getHardFallback(); + if (hardFallback != null) { + return this.getStateRenderTokens(hardFallback); + } + + if (componentState == ComponentState.ENABLED) { + return getMutedContainerTokens(); + } + if (componentState.isDisabled()) { + // TODO: TONAL - revisit + return getMutedContainerTokens(); + } + return defaultActive; + } + }; + } + + private static ContainerRenderColorTokens overlay(ContainerRenderColorTokens original, Color overlay, + int overlayAmount) { + Color overlayWithAlpha = RadianceColorUtilities.getAlphaColor(overlay, overlayAmount); + + // Apply overlay on the container tokens + Color containerLowest = RadianceColorUtilities.overlayColor( + original.getContainerColorTokens().getContainerLowest(), overlayWithAlpha); + Color containerLow = RadianceColorUtilities.overlayColor( + original.getContainerColorTokens().getContainerLow(), overlayWithAlpha); + Color container = RadianceColorUtilities.overlayColor( + original.getContainerColorTokens().getContainer(), overlayWithAlpha); + Color containerHigh = RadianceColorUtilities.overlayColor( + original.getContainerColorTokens().getContainerHigh(), overlayWithAlpha); + Color containerHighest = RadianceColorUtilities.overlayColor( + original.getContainerColorTokens().getContainerHighest(), overlayWithAlpha); + + // Leave on container and container outline tokens as they are + Color onContainer = original.getOnContainerColorTokens().getOnContainer(); + Color onContainerVariant = original.getOnContainerColorTokens().getOnContainerVariant(); + Color containerOutline = original.getContainerOutlineColorTokens().getContainerOutline(); + Color containerOutlineVariant = original.getContainerOutlineColorTokens().getContainerOutlineVariant(); + + return new ContainerRenderColorTokens() { + @Override + public ContainerColorTokens getContainerColorTokens() { + return new ContainerColorTokens() { + @Override + public Color getContainerLowest() { + return containerLowest; + } + + @Override + public Color getContainerLow() { + return containerLow; + } + + @Override + public Color getContainer() { + return container; + } + + @Override + public Color getContainerHigh() { + return containerHigh; + } + + @Override + public Color getContainerHighest() { + return containerHighest; + } + }; + } + + @Override + public OnContainerColorTokens getOnContainerColorTokens() { + return new OnContainerColorTokens() { + @Override + public Color getOnContainer() { + return onContainer; + } + + @Override + public Color getOnContainerVariant() { + return onContainerVariant; + } + }; + } + + @Override + public ContainerOutlineColorTokens getContainerOutlineColorTokens() { + return new ContainerOutlineColorTokens() { + @Override + public Color getContainerOutline() { + return containerOutline; + } + + @Override + public Color getContainerOutlineVariant() { + return containerOutlineVariant; + } + }; } }; } diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/blade/BladeUtils.java b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/blade/BladeUtils.java index a8bd3dfb0..349d24b94 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/blade/BladeUtils.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/blade/BladeUtils.java @@ -684,5 +684,4 @@ public static void populateColorScheme( bladeColorScheme.displayName = nameBuilder.toString(); } - } diff --git a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/RadianceColorUtilities.java b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/RadianceColorUtilities.java index 9957764d4..00b5da51a 100644 --- a/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/RadianceColorUtilities.java +++ b/theming/src/main/java/org/pushingpixels/radiance/theming/internal/utils/RadianceColorUtilities.java @@ -154,6 +154,33 @@ public static Color getInterpolatedColor(Color color1, Color color2, return new Color(getInterpolatedRGB(color1, color2, color1Likeness), true); } + public static Color overlayColor(Color base, Color overlay) { + float baseAlpha = base.getAlpha() / 255.0f; + float overlayAlpha = overlay.getAlpha() / 255.0f; + float finalAlpha = overlayAlpha + baseAlpha * (1.0f - overlayAlpha); + + if (finalAlpha == 0.0f) { + return new Color(0, 0, 0, 0); + } + + int baseR = base.getRed(); + int overlayR = overlay.getRed(); + int finalR = (int) ((overlayR * overlayAlpha + + (baseR * baseAlpha) * (1.0f - overlayAlpha)) / finalAlpha); + + int baseG = base.getGreen(); + int overlayG = overlay.getGreen(); + int finalG = (int) ((overlayG * overlayAlpha + + (baseG * baseAlpha) * (1.0f - overlayAlpha)) / finalAlpha); + + int baseB = base.getBlue(); + int overlayB = overlay.getBlue(); + int finalB = (int) ((overlayB * overlayAlpha + + (baseB * baseAlpha) * (1.0f - overlayAlpha)) / finalAlpha); + + return new Color(finalR, finalG, finalB, (int) (255 * finalAlpha)); + } + /** * Inverts the specified color. *