Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 61 additions & 7 deletions src/gui_common/CustomDropDown.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,16 @@ public void CreateElements()
{
if (item.Checkable)
{
Popup.AddIconCheckItem(item.Icon, item.Text, id);
// Use a pre-tinted icon so the species color is applied only to the custom icon
// while the radio/checkbox indicator (selection control) keeps theme colors
var tintedIcon = item.PreTintedIcon ??= CreateTintedIcon(item.Icon, item.Color);

Popup.AddIconCheckItem(tintedIcon, item.Text, id);
var index = Popup.GetItemIndex(id);
Popup.SetItemIconMaxWidth(index, iconMaxWidth);
Popup.SetItemIconModulate(index, item.Color);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon reading that this SetItemIconModulate is from Godot, I think this might actually be an engine bug that should be reported that Godot 4 now applies icon modulation to the radiobutton as well rather than the icon only.


// Don't apply color modulation to checkable items as it affects the radio button icon
// The species color should only affect the custom icon, not the selection radio button
Popup.SetItemChecked(index, item.Checked);
}
else
Expand Down Expand Up @@ -209,15 +215,51 @@ public void CreateElements()
ReadjustRectSizes();
}

protected override void Dispose(bool disposing)
/// <summary>
/// Creates a tinted copy of the provided icon by multiplying its pixels with the given color.
/// This bakes the color into the texture so later UI modulation won't affect selection indicators.
/// </summary>
/// <param name="icon">Base icon texture to tint</param>
/// <param name="tint">Tint color to apply</param>
/// <returns>New texture containing the tinted icon</returns>
private static Texture2D CreateTintedIcon(Texture2D icon, Color tint)
{
if (disposing)
// Get a mutable image copy of the original texture
var image = icon.GetImage();

if (image == null)
return icon;

// Ensure a format with alpha channel to retain transparency
if (image.GetFormat() != Image.Format.Rgba8 && image.GetFormat() != Image.Format.Rgb8)
{
vSeparationReference.Dispose();
themeVSeparationReference.Dispose();
image.Convert(Image.Format.Rgba8);
}

base.Dispose(disposing);

var width = image.GetWidth();
var height = image.GetHeight();

for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var px = image.GetPixel(x, y);

// Multiply RGB by tint, alpha by tint alpha to allow dimming if needed
var tinted = new Color(
px.R * tint.R,
px.G * tint.G,
px.B * tint.B,
px.A * tint.A);

image.SetPixel(x, y, tinted);
}
}


// Create a texture from the modified image
return ImageTexture.CreateFromImage(image);
}

/// <summary>
Expand Down Expand Up @@ -245,6 +287,17 @@ private void OnPopupAboutToShow()
tween.TweenProperty(Popup, themeVSeparationReference, cachedPopupVSeparation, 0.1).From(-14);
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
vSeparationReference.Dispose();
themeVSeparationReference.Dispose();
}

base.Dispose(disposing);
}

/// <summary>
/// Helper data regarding the popup menu item. All custom operations relating to the dropdown uses this,
/// we can't utilize PopupMenu's internal item class since it's not exposed to the user.
Expand All @@ -264,5 +317,6 @@ public class Item
public bool Checked;
public int Id;
public bool Separator;
public Texture2D? PreTintedIcon;
}
}