Skip to content
Merged
Show file tree
Hide file tree
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/Features/DynamicCubemaps.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ struct DynamicCubemaps : Feature
virtual inline std::string GetName() override { return "Dynamic Cubemaps"; }
virtual inline std::string GetShortName() override { return "DynamicCubemaps"; }
virtual inline std::string_view GetShaderDefineName() override { return "DYNAMIC_CUBEMAPS"; }
virtual std::string_view GetCategory() const override { return "Lighting"; }
virtual std::string_view GetCategory() const override { return "Materials"; }
virtual std::pair<std::string, std::vector<std::string>> GetFeatureSummary() override
{
return {
Expand Down
2 changes: 1 addition & 1 deletion src/Features/ExtendedMaterials.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct ExtendedMaterials : Feature
virtual inline std::string GetName() override { return "Extended Materials"; }
virtual inline std::string GetShortName() override { return "ExtendedMaterials"; }
virtual inline std::string_view GetShaderDefineName() override { return "EXTENDED_MATERIALS"; }
virtual std::string_view GetCategory() const override { return "Landscape & Textures"; }
virtual std::string_view GetCategory() const override { return "Materials"; }

virtual std::pair<std::string, std::vector<std::string>> GetFeatureSummary() override
{
Expand Down
2 changes: 1 addition & 1 deletion src/Features/ExtendedTranslucency.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct ExtendedTranslucency final : Feature
virtual inline std::string GetName() override { return "Extended Translucency"; }
virtual inline std::string GetShortName() override { return "ExtendedTranslucency"; }
virtual inline std::string_view GetShaderDefineName() override { return "EXTENDED_TRANSLUCENCY"; }
virtual inline std::string_view GetCategory() const override { return "Lighting"; }
virtual inline std::string_view GetCategory() const override { return "Materials"; }
virtual bool HasShaderDefine(RE::BSShader::Type shaderType) override { return RE::BSShader::Type::Lighting == shaderType; };
virtual void PostPostLoad() override;
virtual void DrawSettings() override;
Expand Down
2 changes: 1 addition & 1 deletion src/Features/Skylighting.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ struct Skylighting : Feature
virtual inline std::string GetShortName() override { return "Skylighting"; }
virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); }
virtual inline std::string_view GetShaderDefineName() override { return "SKYLIGHTING"; }
virtual std::string_view GetCategory() const override { return "Sky"; }
virtual std::string_view GetCategory() const override { return "Lighting"; }
virtual std::pair<std::string, std::vector<std::string>> GetFeatureSummary() override
{
return {
Expand Down
4 changes: 3 additions & 1 deletion src/Features/VR.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ struct VR : OverlayFeature

virtual void DrawSettings() override;

virtual std::string_view GetCategory() const override { return "Debug"; }

//=============================================================================
// OVERLAY FEATURE OVERRIDES
//=============================================================================
Expand Down Expand Up @@ -580,4 +582,4 @@ struct VR : OverlayFeature
//=============================================================================

void CleanupOverlayTextures();
};
};
10 changes: 9 additions & 1 deletion src/Menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ Menu::~Menu()
uiIcons.loadSettings.Release();
uiIcons.clearCache.Release();
uiIcons.logo.Release();
uiIcons.characters.Release();
uiIcons.grass.Release();
uiIcons.lighting.Release();
uiIcons.sky.Release();
uiIcons.landscape.Release();
uiIcons.water.Release();
uiIcons.debug.Release();
uiIcons.materials.Release();

ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
Expand Down Expand Up @@ -1025,7 +1033,7 @@ void Menu::DrawSettings()
}

// Define category order
std::vector<std::string> categoryOrder = { "Characters", "Grass", "Lighting", "Sky", "Landscape & Textures", "Water", "Other" };
std::vector<std::string> categoryOrder = { "Debug", "Characters", "Grass", "Lighting", "Materials", "Sky", "Landscape & Textures", "Water", "Other" };
// Add categorized features to menu with collapsible headers
for (const std::string& category : categoryOrder) {
if (categorizedFeatures.find(category) != categorizedFeatures.end() && !categorizedFeatures[category].empty()) {
Expand Down
10 changes: 10 additions & 0 deletions src/Menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ class Menu
UIIcon clearCache;
UIIcon logo; // New logo icon
UIIcon search; // Search icon for search bars

// Category icons
UIIcon characters;
UIIcon grass;
UIIcon lighting;
UIIcon sky;
UIIcon landscape;
UIIcon water;
UIIcon debug;
UIIcon materials;
} uiIcons;

struct ThemeSettings
Expand Down
138 changes: 131 additions & 7 deletions src/Utils/UI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,19 @@ namespace Util
logger::info("InitializeMenuIcons: Loading icons from base path: {}", basePath);

// Initialize all texture pointers to nullptr for safe cleanup
std::array<ID3D11ShaderResourceView**, 4> texturePointers = {
std::array<ID3D11ShaderResourceView**, 12> texturePointers = {
&menu->uiIcons.saveSettings.texture,
&menu->uiIcons.loadSettings.texture,
&menu->uiIcons.clearCache.texture,
&menu->uiIcons.logo.texture
&menu->uiIcons.logo.texture,
&menu->uiIcons.characters.texture,
&menu->uiIcons.grass.texture,
&menu->uiIcons.lighting.texture,
&menu->uiIcons.sky.texture,
&menu->uiIcons.landscape.texture,
&menu->uiIcons.water.texture,
&menu->uiIcons.debug.texture,
&menu->uiIcons.materials.texture
};

// Safely release existing textures
Expand Down Expand Up @@ -218,11 +226,78 @@ namespace Util
logger::warn("InitializeMenuIcons: Failed to load logo icon from: {}", basePath + "Community Shaders Logo\\cs-logo.png");
}

logger::info("InitializeMenuIcons: Loaded {}/4 icons successfully", iconsLoaded);
// Load category icons
if (LoadTextureFromFile(device, (basePath + "Categories\\characters.png").c_str(), &menu->uiIcons.characters.texture, menu->uiIcons.characters.size)) {
logger::info("InitializeMenuIcons: Successfully loaded characters icon");
iconsLoaded++;
anyIconLoaded = true;
} else {
logger::warn("InitializeMenuIcons: Failed to load characters icon from: {}", basePath + "Categories\\characters.png");
}

if (LoadTextureFromFile(device, (basePath + "Categories\\grass.png").c_str(), &menu->uiIcons.grass.texture, menu->uiIcons.grass.size)) {
logger::info("InitializeMenuIcons: Successfully loaded grass icon");
iconsLoaded++;
anyIconLoaded = true;
} else {
logger::warn("InitializeMenuIcons: Failed to load grass icon from: {}", basePath + "Categories\\grass.png");
}

if (LoadTextureFromFile(device, (basePath + "Categories\\lighting.png").c_str(), &menu->uiIcons.lighting.texture, menu->uiIcons.lighting.size)) {
logger::info("InitializeMenuIcons: Successfully loaded lighting icon");
iconsLoaded++;
anyIconLoaded = true;
} else {
logger::warn("InitializeMenuIcons: Failed to load lighting icon from: {}", basePath + "Categories\\lighting.png");
}

if (LoadTextureFromFile(device, (basePath + "Categories\\sky.png").c_str(), &menu->uiIcons.sky.texture, menu->uiIcons.sky.size)) {
logger::info("InitializeMenuIcons: Successfully loaded sky icon");
iconsLoaded++;
anyIconLoaded = true;
} else {
logger::warn("InitializeMenuIcons: Failed to load sky icon from: {}", basePath + "Categories\\sky.png");
}

if (LoadTextureFromFile(device, (basePath + "Categories\\landscape.png").c_str(), &menu->uiIcons.landscape.texture, menu->uiIcons.landscape.size)) {
logger::info("InitializeMenuIcons: Successfully loaded landscape icon");
iconsLoaded++;
anyIconLoaded = true;
} else {
logger::warn("InitializeMenuIcons: Failed to load landscape icon from: {}", basePath + "Categories\\landscape.png");
}

if (LoadTextureFromFile(device, (basePath + "Categories\\water.png").c_str(), &menu->uiIcons.water.texture, menu->uiIcons.water.size)) {
logger::info("InitializeMenuIcons: Successfully loaded water icon");
iconsLoaded++;
anyIconLoaded = true;
} else {
logger::warn("InitializeMenuIcons: Failed to load water icon from: {}", basePath + "Categories\\water.png");
}

if (LoadTextureFromFile(device, (basePath + "Categories\\debug.png").c_str(), &menu->uiIcons.debug.texture, menu->uiIcons.debug.size)) {
logger::info("InitializeMenuIcons: Successfully loaded debug icon");
iconsLoaded++;
anyIconLoaded = true;
} else {
logger::warn("InitializeMenuIcons: Failed to load debug icon from: {}", basePath + "Categories\\debug.png");
}

// Materials placeholder - will fail to load for now
if (LoadTextureFromFile(device, (basePath + "Categories\\materials.png").c_str(), &menu->uiIcons.materials.texture, menu->uiIcons.materials.size)) {
logger::info("InitializeMenuIcons: Successfully loaded materials icon");
iconsLoaded++;
anyIconLoaded = true;
} else {
logger::warn("InitializeMenuIcons: Failed to load materials icon from: {}", basePath + "Categories\\materials.png");
}

logger::info("InitializeMenuIcons: Loaded {}/12 icons successfully", iconsLoaded);

return anyIconLoaded;
}

// Text rendering helpers (moved from UITextHelper)
// Text rendering helpers
ImVec2 DrawSharpText(const char* text, bool alignToPixelGrid, float scale)
{
ImVec2 startPos = ImGui::GetCursorPos();
Expand Down Expand Up @@ -340,18 +415,52 @@ namespace Util

bool DrawCategoryHeader(const char* categoryName, bool& isExpanded, int categoryCount)
{
// Get the appropriate icon for this category
ID3D11ShaderResourceView* categoryIcon = nullptr;
auto& menu = Menu::GetSingleton()->uiIcons;

if (strcmp(categoryName, "Characters") == 0) {
categoryIcon = menu.characters.texture;
} else if (strcmp(categoryName, "Grass") == 0) {
categoryIcon = menu.grass.texture;
} else if (strcmp(categoryName, "Lighting") == 0) {
categoryIcon = menu.lighting.texture;
} else if (strcmp(categoryName, "Sky") == 0) {
categoryIcon = menu.sky.texture;
} else if (strcmp(categoryName, "Landscape & Textures") == 0) {
categoryIcon = menu.landscape.texture;
} else if (strcmp(categoryName, "Water") == 0) {
categoryIcon = menu.water.texture;
} else if (strcmp(categoryName, "Debug") == 0) {
categoryIcon = menu.debug.texture;
} else if (strcmp(categoryName, "Materials") == 0) {
categoryIcon = menu.materials.texture;
}
Comment on lines +422 to +438
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Jul 23, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add icon mapping for "Other" category.

The icon selection logic should include handling for the "Other" category to ensure all categories have proper icon display.

 } else if (strcmp(categoryName, "Materials") == 0) {
     categoryIcon = menu.materials.texture;
+} else if (strcmp(categoryName, "Other") == 0) {
+    categoryIcon = menu.other.texture;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (strcmp(categoryName, "Characters") == 0) {
categoryIcon = menu.characters.texture;
} else if (strcmp(categoryName, "Grass") == 0) {
categoryIcon = menu.grass.texture;
} else if (strcmp(categoryName, "Lighting") == 0) {
categoryIcon = menu.lighting.texture;
} else if (strcmp(categoryName, "Sky") == 0) {
categoryIcon = menu.sky.texture;
} else if (strcmp(categoryName, "Landscape & Textures") == 0) {
categoryIcon = menu.landscape.texture;
} else if (strcmp(categoryName, "Water") == 0) {
categoryIcon = menu.water.texture;
} else if (strcmp(categoryName, "Debug") == 0) {
categoryIcon = menu.debug.texture;
} else if (strcmp(categoryName, "Materials") == 0) {
categoryIcon = menu.materials.texture;
}
if (strcmp(categoryName, "Characters") == 0) {
categoryIcon = menu.characters.texture;
} else if (strcmp(categoryName, "Grass") == 0) {
categoryIcon = menu.grass.texture;
} else if (strcmp(categoryName, "Lighting") == 0) {
categoryIcon = menu.lighting.texture;
} else if (strcmp(categoryName, "Sky") == 0) {
categoryIcon = menu.sky.texture;
} else if (strcmp(categoryName, "Landscape & Textures") == 0) {
categoryIcon = menu.landscape.texture;
} else if (strcmp(categoryName, "Water") == 0) {
categoryIcon = menu.water.texture;
} else if (strcmp(categoryName, "Debug") == 0) {
categoryIcon = menu.debug.texture;
} else if (strcmp(categoryName, "Materials") == 0) {
categoryIcon = menu.materials.texture;
} else if (strcmp(categoryName, "Other") == 0) {
categoryIcon = menu.other.texture;
}
🤖 Prompt for AI Agents
In src/Utils/UI.cpp around lines 431 to 447, the icon selection logic lacks a
case for the "Other" category, causing it to miss an appropriate icon. Add an
else-if condition to check if categoryName equals "Other" and assign the
corresponding icon from menu.other.texture to categoryIcon to ensure all
categories have proper icon display.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Maybe a problem?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

Let me investigate whether the "Other" category is actually used in the codebase and if the missing mapping would cause issues.


🏁 Script executed:

# Search for "Other" category usage in the codebase
rg '"Other"' -A 2 -B 2

# Look for category definitions and usage patterns
rg 'GetCategory.*return' -A 1 -B 1

# Check what categories are defined in Menu.cpp predefined order
rg 'predefinedOrder.*=|predefinedOrder' -A 10 -B 2

Length of output: 9565


@doodlum You're right to question this! After investigating the codebase, this is actually a problem. Here's what I found:

The search results show that:

  1. "Other" is a real category - it's included in the predefined category order in Menu.cpp: "Debug", "Characters", "Grass", "Lighting", "Materials", "Sky", "Landscape & Textures", "Water", "Other"

  2. "Other" is the default fallback - in Feature.h, the base GetCategory() method returns "Other" by default for any features that don't override it

  3. The UI will display "Other" category headers - since it's in the category order, but without the icon mapping, these headers won't show an icon while all other categories will

This creates inconsistent UI behavior where some category headers have icons and others don't, even though the menu.other.texture icon exists (as mentioned in the AI summary).

The missing mapping means any features using the default "Other" category will have headers without icons, which breaks the visual consistency of the interface. My original suggestion to add the "Other" category mapping is indeed necessary for proper UI functionality.


🏁 Script executed:

# Check if "other" icon is loaded in InitializeMenuIcons
rg "other\." src/Utils/UI.cpp -A 2 -B 2

# Verify if there's an "other" icon file that should be loaded
fd "other.png" --type f

Length of output: 65


Let me check what's actually defined in the Menu.h structure and what category icon files exist:


🏁 Script executed:

# Check the UIIcons struct definition in Menu.h
rg "struct UIIcons|UIIcon.*other|UIIcon.*materials" src/Menu.h -A 10 -B 2

# List all icon files in the Categories directory
fd . "package/Interface/CommunityShaders/Icons/Categories" --type f

# Check if there are any icon files with "other" in the name
fd "other" --type f

Length of output: 1507


Perfect! Now I can see the full scope of the issue. You're absolutely right to be concerned - this is a problem, and it's actually bigger than just the missing mapping.

Here's what I found:

  1. The other icon IS defined in Menu.h: UIIcon other; // Generic icon for other/misc categories

  2. But the other.png file doesn't exist - the Categories directory has icons for all other categories (characters, debug, grass, landscape, lighting, materials, sky, water) but no other.png

  3. The loading code is also missing - InitializeMenuIcons doesn't attempt to load the other icon

  4. And the mapping is missing - which is what my original suggestion addressed

So there are actually three missing pieces:

  • The other.png icon file itself
  • Loading the icon in InitializeMenuIcons
  • Mapping the icon in DrawCategoryHeader

This means any features using the default "Other" category (which is the fallback for features that don't specify a category) will have inconsistent header display compared to other categories.

My original suggestion to add the mapping is still valid and necessary - even if the icon file doesn't exist yet, the mapping should be there so when the icon is eventually added, it will work properly. The code gracefully handles missing textures (nullptr), so adding the mapping won't break anything.

The issue affects UI consistency since "Other" is a real category that appears in the interface but won't display an icon like all the others do.


🧬 Code Graph Analysis Results

Relevant snippets from src/Utils/UI.h (lines 53-350 approx)

This header declares the classes and functions implemented in src/Utils/UI.cpp. It includes:

  • HoverTooltipWrapper class: RAII wrapper for ImGui tooltips on hovered items.
  • DisableGuard class: RAII wrapper for ImGui disabled state.
  • StyledButtonWrapper class: RAII wrapper for pushing/popping button style colors.
  • SectionWrapper class: RAII wrapper for drawing section headers with optional description and color.
  • Functions for UI elements:
    • PercentageSlider: slider for float values shown as percentages.
    • GetNativeViewportSizeScaled: returns scaled viewport size.
    • LoadTextureFromFile: loads a texture from file into a D3D11 shader resource view.
    • InitializeMenuIcons: loads multiple icons for the menu.
    • DrawSharpText: draws text aligned to pixel grid with optional scaling.
    • DrawAlignedTextWithLogo: draws text with a logo icon aligned vertically.
    • DrawCategoryHeader: draws a collapsible category header with icon and lines.
    • DrawSectionHeader: draws a styled section header, optionally collapsible.
    • ColorCodedValueConfig struct and related functions for color-coded value display.
    • DrawColorCodedValue: draws a label and a color-coded value with optional tooltip.
    • DrawMultiLineTooltip and DrawColoredMultiLineTooltip: draw multi-line tooltips with optional colors.
    • Table sorting and rendering helpers:
      • ShowSortedStringTableStrings: renders a sortable ImGui table for string data.
      • ShowSortedStringTableCustom: templated function to render sortable tables for custom row types.

These declarations provide context for the implementations in UI.cpp.


Relevant snippets from src/Menu.h (lines 16-20)

static Menu* GetSingleton()
{
	static Menu menu;
	return &menu;
}

This singleton accessor is used in UI.cpp to get the global menu instance for accessing UI icons and theme colors.


Relevant snippets from src/Menu.cpp (lines 721-732)

void operator()(const CategoryHeader& header)
{
	// Get expansion state from static map
	bool isExpanded = categoryExpansionStates[header.name];

	// Draw category header with custom styling using util:UI function
	int count = categoryCounts[std::string(header.name)];
	Util::DrawCategoryHeader(header.name.c_str(), isExpanded, count);

	// Update expansion state
	categoryExpansionStates[header.name] = isExpanded;
}

This snippet shows usage of Util::DrawCategoryHeader from UI.cpp to render category headers with expansion state.


Relevant snippets from src/Menu.cpp (lines 733-779)

void operator()(Feature* feat)
{
	const auto featureName = feat->GetShortName();
	bool isDisabled = globals::state->IsFeatureDisabled(featureName);
	bool isLoaded = feat->loaded;
	bool hasFailedMessage = !feat->failedLoadedMessage.empty();
	auto& themeSettings = globals::menu->settings.Theme;

	ImVec4 textColor;

	// Determine the text color based on the state
	if (isDisabled) {
		textColor = themeSettings.StatusPalette.Disable;
	} else if (isLoaded) {
		textColor = ImGui::GetStyleColorVec4(ImGuiCol_Text);
	} else if (hasFailedMessage) {
		textColor = feat->version.empty() ? themeSettings.StatusPalette.Disable : themeSettings.StatusPalette.Error;
	} else {
		// No failed message but not loaded - check if INI file exists
		if (!std::filesystem::exists(Util::PathHelpers::GetFeatureIniPath(feat->GetShortName()))) {
			// INI file missing - treat as missing feature (grey)
			textColor = themeSettings.StatusPalette.Disable;
		} else {
			// INI file exists but feature not loaded - truly pending restart (green)
			textColor = themeSettings.StatusPalette.RestartNeeded;
		}
	}

	// Set text color
	ImGui::PushStyleColor(ImGuiCol_Text, textColor);

	// Create selectable item
	if (ImGui::Selectable(fmt::format(" {} ", feat->GetName()).c_str(), selectedMenuRef == listId, ImGuiSelectableFlags_SpanAllColumns)) {
		selectedMenuRef = listId;
	}

	// Restore original text color
	ImGui::PopStyleColor();

	// Display version if loaded
	if (isLoaded) {
		ImGui::SameLine();
		std::string formattedVersion = feat->version;
		std::replace(formattedVersion.begin(), formattedVersion.end(), '-', '.');
		ImGui::TextDisabled(fmt::format("({})", formattedVersion).c_str());
	}
}

This snippet shows feature list rendering using ImGui, with color coding and selectable items. It uses Util::PathHelpers::GetFeatureIniPath and theme colors from the menu singleton.


Relevant snippets from src/Menu.cpp (lines 800-1007)

This large snippet shows detailed UI rendering for a feature's settings and about tabs, including:

  • Handling disabled, loaded, failed, and unloaded states.
  • Drawing feature settings or unloaded UI.
  • Showing error messages.
  • About tab with status and description.
  • Buttons for enabling/disabling at boot and restoring defaults.
  • Tooltips using Util::HoverTooltipWrapper.
  • Uses Util::PathHelpers::GetFeatureIniPath and theme colors.

Summary of relevance

  • The UI.cpp file implements many UI helper functions and wrappers used throughout the UI code.
  • The InitializeMenuIcons function loads icons used in category headers and elsewhere.
  • The DrawCategoryHeader and DrawSectionHeader functions provide styled headers with icons and lines.
  • The HoverTooltipWrapper is used for tooltips in multiple places.
  • The Menu singleton is accessed for icons and theme colors.
  • The Menu.cpp snippets show usage of these UI helpers for rendering feature lists and category headers.
  • The PathHelpers::GetFeatureIniPath function is referenced for checking feature INI files, relevant to feature UI states.

No other snippets from the provided list appear directly relevant to understanding or addressing the code in src/Utils/UI.cpp.


🧠 Learnings used
Learnt from: davo0411
PR: doodlum/skyrim-community-shaders#1107
File: src/Menu.cpp:310-315
Timestamp: 2025-05-31T02:51:24.195Z
Learning: In the Community Shaders codebase, when validating UI icons, checking for texture presence (non-null pointer) is sufficient. Avoid adding dimension validation checks as icon sizes should vary based on user needs (4K+ resolution users may want higher resolution icons). Don't artificially limit icon dimensions.

Learnt from: alandtse
PR: doodlum/skyrim-community-shaders#0
File: :0-0
Timestamp: 2025-06-24T07:17:36.604Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.


// Add categoryCount to categoryName
std::string displayName = std::format("{} ({})", categoryName, categoryCount);

// Draw category header with custom styling
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 pos = ImGui::GetCursorScreenPos();
float availableWidth = ImGui::GetContentRegionAvail().x;

// Calculate icon size based on current font size to match text scaling
// This ensures icons scale consistently with text when the font scale changes
const float currentFontSize = ImGui::GetFontSize();
const float iconSize = currentFontSize * 1.2f; // 20% larger than font height
const float iconSpacing = currentFontSize * 0.3f; // 30% of font height for spacing
ImVec2 textSize = ImGui::CalcTextSize(displayName.c_str());

// Calculate total content width (icon + spacing + text)
float contentWidth = textSize.x;
if (categoryIcon) {
contentWidth += iconSize + iconSpacing;
}

// Calculate line positions
float lineY = pos.y + textSize.y * 0.5f;
float lineLength = (availableWidth - textSize.x - 20.0f) * 0.5f; // 20px for padding
float lineLength = (availableWidth - contentWidth - 20.0f) * 0.5f; // 20px for padding

// Create selectable area for the entire header
ImGui::PushID(displayName.c_str());
Expand Down Expand Up @@ -382,13 +491,28 @@ namespace Util
}

// Right line
float rightLineStart = pos.x + lineLength + 10.0f + textSize.x + 10.0f;
float rightLineStart = pos.x + lineLength + 10.0f + contentWidth + 10.0f;
if (rightLineStart < pos.x + availableWidth) {
drawList->AddLine(ImVec2(rightLineStart, lineY), ImVec2(pos.x + availableWidth, lineY), headerColor, 1.0f);
}

// Draw icon and text
float currentX = pos.x + lineLength + 10.0f;

// Draw icon if available
if (categoryIcon) {
ImVec2 iconPos = ImVec2(currentX, pos.y + (textSize.y - iconSize) * 0.5f + 2.0f);
ImVec2 iconMax = ImVec2(iconPos.x + iconSize, iconPos.y + iconSize);

// Apply the same color tint as the text
ImU32 iconTint = headerColor;
drawList->AddImage(categoryIcon, iconPos, iconMax, ImVec2(0, 0), ImVec2(1, 1), iconTint);

currentX += iconSize + iconSpacing;
}

// Center text
ImVec2 textPos = ImVec2(pos.x + lineLength + 10.0f, pos.y + 2.0f);
ImVec2 textPos = ImVec2(currentX, pos.y + 2.0f);
drawList->AddText(textPos, headerColor, displayName.c_str());

// Handle click to toggle expansion
Expand Down