getTone method
Implementation
@override
double getTone(DynamicScheme scheme, DynamicColor color) {
final toneDeltaPair = color.toneDeltaPair?.call(scheme);
// Case 0: tone delta pair.
if (toneDeltaPair != null) {
final roleA = toneDeltaPair.roleA;
final roleB = toneDeltaPair.roleB;
final polarity = toneDeltaPair.polarity;
final constraint = toneDeltaPair.constraint;
final absoluteDelta =
polarity == .darker ||
(polarity == .relativeLighter && scheme.isDark) ||
(polarity == .relativeDarker && !scheme.isDark)
? -toneDeltaPair.delta
: toneDeltaPair.delta;
final amRoleA = color.name == roleA.name;
final selfRole = amRoleA ? roleA : roleB;
final referenceRole = amRoleA ? roleB : roleA;
var selfTone = selfRole.tone(scheme);
final referenceTone = referenceRole.getTone(scheme);
final relativeDelta = absoluteDelta * (amRoleA ? 1.0 : -1.0);
switch (constraint) {
case .exact:
selfTone = MathUtils.clampDouble(
0.0,
100.0,
referenceTone + relativeDelta,
);
break;
case .nearer:
if (relativeDelta > 0.0) {
selfTone = MathUtils.clampDouble(
0.0,
100.0,
MathUtils.clampDouble(
referenceTone,
referenceTone + relativeDelta,
selfTone,
),
);
} else {
selfTone = MathUtils.clampDouble(
0.0,
100.0,
MathUtils.clampDouble(
referenceTone + relativeDelta,
referenceTone,
selfTone,
),
);
}
break;
case .farther:
if (relativeDelta > 0.0) {
selfTone = MathUtils.clampDouble(
referenceTone + relativeDelta,
100.0,
selfTone,
);
} else {
selfTone = MathUtils.clampDouble(
0.0,
referenceTone + relativeDelta,
selfTone,
);
}
break;
}
if (color.background != null && color.contrastCurve != null) {
final background = color.background!(scheme);
final contrastCurve = color.contrastCurve!(scheme);
if (background != null && contrastCurve != null) {
final bgTone = background.getTone(scheme);
final selfContrast = contrastCurve.get(scheme.contrastLevel);
selfTone =
Contrast.ratioOfTones(bgTone, selfTone) >= selfContrast &&
scheme.contrastLevel >= 0.0
? selfTone
: DynamicColor.foregroundTone(bgTone, selfContrast);
}
}
// This can avoid the awkward tones for background colors including the access fixed colors.
// Accent fixed dim colors should not be adjusted.
if (color.isBackground && !color.name.endsWith("_fixed_dim")) {
if (selfTone >= 57) {
selfTone = MathUtils.clampDouble(65.0, 100.0, selfTone);
} else {
selfTone = MathUtils.clampDouble(0.0, 49.0, selfTone);
}
}
return selfTone;
} else {
// Case 1: No tone delta pair; just solve for itself.
double answer = color.tone(scheme);
if (color.background?.call(scheme) == null ||
color.contrastCurve?.call(scheme) == null) {
return answer; // No adjustment for colors with no background.
}
final bgTone = color.background!(scheme)!.getTone(scheme);
final desiredRatio = color.contrastCurve!(scheme)!.get(
scheme.contrastLevel,
);
// Recalculate the tone from desired contrast ratio if the current
// contrast ratio is not enough or desired contrast level is decreasing
// (<0).
answer =
Contrast.ratioOfTones(bgTone, answer) >= desiredRatio &&
scheme.contrastLevel >= 0.0
? answer
: DynamicColor.foregroundTone(bgTone, desiredRatio);
// This can avoid the awkward tones for background colors including the access fixed colors.
// Accent fixed dim colors should not be adjusted.
if (color.isBackground && !color.name.endsWith("_fixed_dim")) {
if (answer >= 57.0) {
answer = MathUtils.clampDouble(65.0, 100.0, answer);
} else {
answer = MathUtils.clampDouble(0.0, 49.0, answer);
}
}
if (color.secondBackground?.call(scheme) == null) {
return answer;
}
// Case 2: Adjust for dual backgrounds.
final bgTone1 = color.background!(scheme)!.getTone(scheme);
final bgTone2 = color.secondBackground!(scheme)!.getTone(scheme);
final upper = math.max(bgTone1, bgTone2);
final lower = math.min(bgTone1, bgTone2);
if (Contrast.ratioOfTones(upper, answer) >= desiredRatio &&
Contrast.ratioOfTones(lower, answer) >= desiredRatio) {
return answer;
}
// The darkest light tone that satisfies the desired ratio,
// or -1 if such ratio cannot be reached.
final lightOption = Contrast.lighter(upper, desiredRatio);
// The lightest dark tone that satisfies the desired ratio,
// or -1 if such ratio cannot be reached.
final darkOption = Contrast.darker(lower, desiredRatio);
// Tones suitable for the foreground.
final availables = <double>[?lightOption, ?darkOption];
final prefersLight =
DynamicColor.tonePrefersLightForeground(bgTone1) ||
DynamicColor.tonePrefersLightForeground(bgTone2);
if (prefersLight) {
return lightOption ?? 100.0;
}
return availables.length == 1 ? availables[0] : (darkOption ?? 0.0);
}
}