Use context-appropriate highlight colors for province borders (#6202)

- Override (faction hover/notification): always black — visible against
  any faction color
- Targeted (right-click): deep purple-red — distinct from red faction
- Selected (left-click): luminance-adaptive — dark factions get white
  highlight, bright factions get black

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 08:25:46 -08:00
committed by GitHub
co-authored by Claude Opus 4.6
parent 13777a1e83
commit ca38c1df3a
@@ -129,18 +129,21 @@ namespace eagle {
public ProvinceIDLoader provinceIDLoader;
private readonly Color _selectionColor = Color.grey;
private readonly Color _targetColor = Color.red;
private readonly Color _commandAvailableColor = Color.blue;
private readonly Color _hoverColor = new Color(0, 0, 0.5f, 0.5f);
private const float SelectedBorderMin = 2f, SelectedBorderMax = 6f,
SelectedBorderCycle = 1.0f;
private static readonly Color SelectedBorderColor = new Color(0f, 0f, 0f, 0.95f);
private static readonly Color OverrideStrobeColor = Color.black;
private static readonly Color OverrideBorderColor = new Color(0.05f, 0.05f, 0.05f, 0.95f);
private const float OverrideBorderMin = 3f, OverrideBorderMax = 8f,
OverrideBorderCycle = 0.25f;
private static readonly Color TargetedStrobeColor = new Color(0.55f, 0.05f, 0.15f);
private static readonly Color TargetedBorderColor = new Color(0.55f, 0.05f, 0.15f, 0.95f);
private const float TargetedBorderMin = 3f, TargetedBorderMax = 8f,
TargetedBorderCycle = 0.25f;
private static readonly Color TargetedBorderColor = new Color(1f, 0.2f, 0.2f, 0.95f);
private const float CommandBorderMin = 1.5f, CommandBorderMax = 5f,
CommandBorderCycle = 0.5f;
@@ -167,6 +170,17 @@ namespace eagle {
return color;
}
private static float Luminance(Color c) {
return 0.299f * c.r + 0.587f * c.g + 0.114f * c.b;
}
private static (Color strobe, Color border) ContrastingHighlightColors(Color baseColor) {
if (Luminance(baseColor) > 0.5f) {
return (Color.black, new Color(0.05f, 0.05f, 0.05f, 0.95f));
}
return (Color.white, new Color(1f, 1f, 1f, 0.95f));
}
private static bool HasBlizzardEvent(ProvinceView province) {
foreach (var evt in province.KnownEvents) {
if (evt.SealedValueCase == ProvinceEvent.SealedValueOneofCase.BlizzardEvent) {
@@ -236,27 +250,29 @@ namespace eagle {
provinceBorderManager.SetHighlightGroup(OverrideTargetedProvinces);
foreach (var pid in OverrideTargetedProvinces) {
FlashProvince(pid, _targetColor, TargetedBorderCycle);
FlashProvince(pid, OverrideStrobeColor, OverrideBorderCycle);
HighlightProvinceBorder(
pid,
TargetedBorderColor,
TargetedBorderMin,
TargetedBorderMax,
TargetedBorderCycle);
OverrideBorderColor,
OverrideBorderMin,
OverrideBorderMax,
OverrideBorderCycle);
}
} else {
provinceBorderManager.ClearHighlightGroup();
if (SelectedProvinceId is ProvinceId sPid) {
FlashProvince(sPid, _selectionColor, SelectedBorderCycle);
var sBase = ColorForProvince(Model.Provinces[sPid]);
var (sStrobe, sBorder) = ContrastingHighlightColors(sBase);
FlashProvince(sPid, sStrobe, SelectedBorderCycle);
HighlightProvinceBorder(
sPid,
SelectedBorderColor,
sBorder,
SelectedBorderMin,
SelectedBorderMax,
SelectedBorderCycle);
}
foreach (ProvinceId tPid in TargetedProvinces) {
FlashProvince(tPid, _targetColor, TargetedBorderCycle);
FlashProvince(tPid, TargetedStrobeColor, TargetedBorderCycle);
HighlightProvinceBorder(
tPid,
TargetedBorderColor,