Skip to content

feat(weather): per-feature settings rework, main feature lock#1704

Merged
davo0411 merged 5 commits into
community-shaders:devfrom
davo0411:weather-feature-fixes
Jan 13, 2026
Merged

feat(weather): per-feature settings rework, main feature lock#1704
davo0411 merged 5 commits into
community-shaders:devfrom
davo0411:weather-feature-fixes

Conversation

@davo0411
Copy link
Copy Markdown
Collaborator

@davo0411 davo0411 commented Jan 13, 2026

  1. locks main feature sliders when using per weather
  2. moves per weather setting manipulation to the weather editor
  3. misc fixes and logic improvements

Summary by CodeRabbit

Release Notes

New Features

  • Weather-aware UI controls now visually indicate when weather overrides settings (grayed out/disabled).
  • Click on weather-controlled settings to open the weather editor for direct adjustment.
  • Per-weather feature settings with toggle between weather-specific and global defaults.
  • Tooltips display when hovering over weather-controlled UI elements.

Documentation

  • Updated weather system documentation with implementation guidance and UI helper details.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 13, 2026

📝 Walkthrough

Walkthrough

This PR introduces weather-aware UI controls and per-weather feature settings management. It adds Util::WeatherUI helpers that grey out when weather overrides apply, enables editor navigation to specific feature settings via OpenWeatherFeatureSetting(), implements per-weather feature settings persistence in WeatherWidget with toggle state, and updates WeatherManager to respect feature-level enablement flags.

Changes

Cohort / File(s) Summary
Weather-Aware UI Helpers
src/Utils/UI.h, src/Utils/UI.cpp
New Util::WeatherUI namespace with 5 helper functions: IsWeatherControlled(), SliderFloat(), Checkbox(), ColorEdit3(), ColorEdit4() that automatically grey out and display tooltips when weather-controlled; enables click-to-edit for weather editor access.
Weather Editor Navigation
src/WeatherEditor/EditorWindow.h, src/WeatherEditor/EditorWindow.cpp
Adds OpenWeatherFeatureSetting() method to locate and focus weather-specific feature settings in the editor; note: method declared/defined twice, indicating potential duplication.
Weather Widget Enhancement
src/WeatherEditor/Weather/WeatherWidget.h, src/WeatherEditor/Weather/WeatherWidget.cpp
Introduces per-weather feature override toggles with per-feature settings containers, NavigateToFeatureSetting() navigation support with pending state tracking, and immediate persistence when active weather is affected; adds UI for bool/float/color editing with Reset-to-Global context menus.
Weather Manager Logic
src/WeatherManager.cpp
Updates LoadSettingsFromWeather() to respect feature-level __enabled flags and strip enablement metadata from output JSON during selective copying.
Documentation & Includes
docs/weather-system-docs/WeatherVariableRegistration.md, src/Feature.cpp
Documents new weather-aware UI workflow; adds includes for WeatherManager.h and WeatherVariableRegistry.h.

Sequence Diagrams

sequenceDiagram
    participant User
    participant UI as Util::WeatherUI<br/>(Control)
    participant EditorWindow
    participant WeatherWidget
    participant WeatherManager

    User->>UI: Interact with weather-controlled slider/checkbox
    UI->>UI: Check IsWeatherControlled()
    alt Is Weather Controlled
        UI->>EditorWindow: OpenWeatherFeatureSetting()
        EditorWindow->>WeatherWidget: Locate & focus widget
        WeatherWidget->>WeatherWidget: NavigateToFeatureSetting()
        WeatherWidget->>User: Display feature settings UI
    else Not Controlled
        UI->>User: Allow edit & return value
    end
Loading
sequenceDiagram
    participant User
    participant WeatherWidget as WeatherWidget<br/>(Settings UI)
    participant WeatherManager
    participant Registry as Weather<br/>Variable Registry

    User->>WeatherWidget: Toggle per-weather override
    User->>WeatherWidget: Edit feature setting
    WeatherWidget->>WeatherManager: SaveWeatherSettings()
    WeatherManager->>WeatherManager: Check __enabled flag
    alt Weather Currently Active
        WeatherManager->>Registry: Apply feature override
        Registry->>User: Live update
    end
    WeatherWidget->>User: Confirm save
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • feat: weather and imagespace editor #1630: Implements weather editor integration with per-feature settings persistence, navigation state management, and UI helper functions that work in tandem with this PR's weather-aware control system.

Suggested reviewers

  • alandtse
  • doodlum
  • jiayev

Poem

🐰 Hops excitedly with oversized pencil

Through weather's veil, the UI now sees,
With toggles soft and greyings with ease,
Per-weather settings bloom and take flight,
The editor hops to settings just right!
A rabbit's delight—controls weather-tight! 🌤️

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: implementing per-feature settings rework and adding main feature lock functionality when weather overrides apply.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Automated formatting by clang-format, prettier, and other hooks.
See https://pre-commit.ci for details.
@github-actions
Copy link
Copy Markdown

Using provided base ref: 9419062
Using base ref: 9419062
Base commit date: 2026-01-13T19:48:52+10:00 (Tuesday, January 13, 2026 07:48 PM)
No actionable suggestions for changed features.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/WeatherEditor/Weather/WeatherWidget.cpp:
- Around line 1635-1642: The Json type conversion calls using
currentValue.get<T>() in the WeatherWidget UI code (e.g., inside the
dynamic_cast to WeatherVariables::WeatherVariable<bool> and the other branches
for float/int/string) can throw nlohmann::json::type_error; wrap those
retrievals in a try/catch or replace them with currentValue.value("<key>",
<default>) to provide a safe default and avoid exceptions, then use the
retrieved safe value for ImGui::Checkbox/Slider/TextInput and only write back to
featureJson[varName] when the UI changes (preserve modified flag behavior).
Ensure you update the bool branch
(dynamic_cast<WeatherVariables::WeatherVariable<bool>>), as well as the
corresponding float/int/string branches referenced in the review, to use the
safe retrieval pattern and handle exceptions consistently.
🧹 Nitpick comments (5)
docs/weather-system-docs/WeatherVariableRegistration.md (1)

113-115: Consider using weather-aware Checkbox in the example for consistency.

The example shows ImGui::Checkbox for the "Enable Effect" setting with a comment saying it's "not weather-controlled in this example." However, since Util::WeatherUI::Checkbox() is listed as available (line 121), it would be clearer to either:

  1. Use Util::WeatherUI::Checkbox to demonstrate consistency, or
  2. Add a brief note explaining when to use the regular ImGui version vs. the weather-aware version

This helps users understand the distinction better.

src/Utils/UI.cpp (2)

1373-1375: Remove unused variable declarations.

Lines 1373-1374 declare weatherManager and currentWeathers but they're not used until lines 1391-1407 inside the click/hover handlers. These declarations are redundant here and can be removed since the same variables are re-declared in the relevant blocks below.

♻️ Suggested fix
 		if (isControlled) {
-			auto* weatherManager = WeatherManager::GetSingleton();
-			auto currentWeathers = weatherManager->GetCurrentWeathers();
-
 			// Make it look like a clickable button when weather-controlled
 			ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.3f, 0.3f, 0.4f, 0.8f));

1424-1579: Consider extracting common weather-override UI logic.

The tooltip rendering (lines 1458-1470, 1509-1521, 1560-1572) and click-to-edit handling (lines 1445-1456, 1496-1507, 1547-1558) are duplicated across all four control functions with identical logic.

Extracting these into private helpers would reduce duplication and simplify maintenance:

♻️ Suggested helper functions
namespace WeatherUI
{
    namespace detail
    {
        void ShowWeatherOverrideTooltip()
        {
            auto* weatherManager = WeatherManager::GetSingleton();
            auto currentWeathers = weatherManager->GetCurrentWeathers();
            ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
            ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "Weather Override Active");
            ImGui::TextWrapped("This setting is controlled by the current weather (%s).",
                currentWeathers.currentWeather ? currentWeathers.currentWeather->GetFormEditorID() : "Unknown");
            ImGui::Separator();
            ImGui::TextColored(ImVec4(0.6f, 0.9f, 0.6f, 1.0f), "Click to open Weather Editor");
            ImGui::PopTextWrapPos();
        }

        void HandleClickToEdit(Feature* feature, const char* settingName)
        {
            auto* weatherManager = WeatherManager::GetSingleton();
            auto* editorWindow = EditorWindow::GetSingleton();
            auto currentWeathers = weatherManager->GetCurrentWeathers();

            if (currentWeathers.currentWeather && editorWindow) {
                editorWindow->OpenWeatherFeatureSetting(
                    currentWeathers.currentWeather,
                    feature->GetShortName(),
                    settingName);
            }
        }
    }
}
src/WeatherEditor/EditorWindow.cpp (1)

1111-1114: Use SetOpen(true) for consistency.

The rest of the codebase uses SetOpen(true) to open widgets (e.g., lines 186, 249, 542). Direct member access to open may bypass any side effects implemented in SetOpen().

Suggested fix
-			// Open the widget if it's not already open
-			if (!weatherWidget->open) {
-				weatherWidget->open = true;
-			}
+			// Open the widget if it's not already open
+			if (!weatherWidget->IsOpen()) {
+				weatherWidget->SetOpen(true);
+			}
src/WeatherEditor/Weather/WeatherWidget.cpp (1)

1759-1767: pendingSettingHighlight is set but never used.

The settingName parameter is stored in pendingSettingHighlight (line 1763), but it's never read in DrawFeatureSettings - only pendingFeatureNavigation is used (line 1537). The clearing at lines 1753-1755 clears both, but pendingSettingHighlight has no effect.

Either implement the highlighting for the specific setting, or simplify the method signature if setting-level navigation isn't needed.

#!/bin/bash
# Verify pendingSettingHighlight is not used elsewhere
rg -n "pendingSettingHighlight" --type=cpp
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9419062 and 9bc65af.

📒 Files selected for processing (9)
  • docs/weather-system-docs/WeatherVariableRegistration.md
  • src/Feature.cpp
  • src/Utils/UI.cpp
  • src/Utils/UI.h
  • src/WeatherEditor/EditorWindow.cpp
  • src/WeatherEditor/EditorWindow.h
  • src/WeatherEditor/Weather/WeatherWidget.cpp
  • src/WeatherEditor/Weather/WeatherWidget.h
  • src/WeatherManager.cpp
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{cpp,cxx,cc,c,h,hpp,hxx,hlsl,hlsli,fx,fxh,py}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not include TODO/FIXME placeholders; provide complete, working solutions

Files:

  • src/WeatherManager.cpp
  • src/WeatherEditor/Weather/WeatherWidget.h
  • src/Feature.cpp
  • src/Utils/UI.h
  • src/WeatherEditor/EditorWindow.cpp
  • src/Utils/UI.cpp
  • src/WeatherEditor/Weather/WeatherWidget.cpp
  • src/WeatherEditor/EditorWindow.h
src/**/*.{cpp,cxx,cc,h,hpp,hxx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

src/**/*.{cpp,cxx,cc,h,hpp,hxx}: Ensure SE/AE/VR runtime compatibility; use runtime detection patterns (e.g., REL::RelocateMember())
Include robust error handling and resource management with graceful degradation in the plugin code

Files:

  • src/WeatherManager.cpp
  • src/WeatherEditor/Weather/WeatherWidget.h
  • src/Feature.cpp
  • src/Utils/UI.h
  • src/WeatherEditor/EditorWindow.cpp
  • src/Utils/UI.cpp
  • src/WeatherEditor/Weather/WeatherWidget.cpp
  • src/WeatherEditor/EditorWindow.h
**/*

⚙️ CodeRabbit configuration file

**/*: When reviewing PRs, please provide suggestions for:

  1. Conventional Commit Titles (if not following https://www.conventionalcommits.org/ or
    if the existing title does not describe the code changes):
    Format: type(scope): description
    Length: 50 characters limit for title, 72 for body
    Style: lowercase description, no ending period
    Examples:

    • feat(vr): add cross-eye sampling
    • fix(water): resolve flowmap bug
    • docs: update shader documentation
  2. Issue References (if PR fixes bugs or implements features):
    Suggest adding appropriate GitHub keywords:

    • "Fixes #123" or "Closes #123" for bug fixes
    • "Implements #123" or "Addresses #123" for features
    • "Related to #123" for partial implementations

Otherwise, use your standard review approach focusing on code quality.

Files:

  • src/WeatherManager.cpp
  • src/WeatherEditor/Weather/WeatherWidget.h
  • src/Feature.cpp
  • src/Utils/UI.h
  • docs/weather-system-docs/WeatherVariableRegistration.md
  • src/WeatherEditor/EditorWindow.cpp
  • src/Utils/UI.cpp
  • src/WeatherEditor/Weather/WeatherWidget.cpp
  • src/WeatherEditor/EditorWindow.h
🧠 Learnings (5)
📓 Common learnings
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 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.
📚 Learning: 2025-08-17T18:37:35.839Z
Learnt from: CR
Repo: doodlum/skyrim-community-shaders PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-17T18:37:35.839Z
Learning: Applies to features/**/*.{cpp,cxx,cc,h,hpp,hxx} : Feature classes must inherit from Feature (src/Feature.h) and implement DrawSettings(), LoadSettings(), and SaveSettings()

Applied to files:

  • src/WeatherEditor/Weather/WeatherWidget.h
  • src/Feature.cpp
  • src/WeatherEditor/Weather/WeatherWidget.cpp
📚 Learning: 2025-08-17T18:37:35.839Z
Learnt from: CR
Repo: doodlum/skyrim-community-shaders PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-08-17T18:37:35.839Z
Learning: Applies to features/**/*.{cpp,cxx,cc,h,hpp,hxx} : Register new features in the globals::features namespace

Applied to files:

  • src/Feature.cpp
📚 Learning: 2025-06-24T07:17:36.604Z
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 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.

Applied to files:

  • src/Feature.cpp
  • src/Utils/UI.h
  • docs/weather-system-docs/WeatherVariableRegistration.md
  • src/WeatherEditor/EditorWindow.cpp
  • src/Utils/UI.cpp
  • src/WeatherEditor/Weather/WeatherWidget.cpp
  • src/WeatherEditor/EditorWindow.h
📚 Learning: 2025-06-17T09:27:49.594Z
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 1157
File: src/Feature.cpp:42-49
Timestamp: 2025-06-17T09:27:49.594Z
Learning: In src/Feature.cpp, when an obsolete feature's INI file is deleted, the feature should be silently disabled without surfacing any issues to the user. This is the intended behavior because a deleted INI file for an obsolete feature indicates that the user has properly cleaned up the obsolete feature.

Applied to files:

  • src/Feature.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build plugin and addons
🔇 Additional comments (11)
src/WeatherEditor/EditorWindow.h (1)

164-166: LGTM!

The OpenWeatherFeatureSetting declaration is well-designed with appropriate parameter types. Using const std::string& for string parameters avoids unnecessary copies.

src/Utils/UI.h (1)

200-238: LGTM! Well-designed weather-aware UI API.

The WeatherUI namespace provides a clean, consistent API that:

  • Mirrors ImGui conventions (const char* labels, bool returns)
  • Documents behavior clearly with doxygen-style comments
  • Maintains symmetry across control types (SliderFloat, Checkbox, ColorEdit3, ColorEdit4)

The IsWeatherControlled utility function is a nice addition for cases where callers need to check override status without rendering a control.

src/Utils/UI.cpp (3)

1337-1366: LGTM!

The IsWeatherControlled implementation has proper null checks and a clear logical flow: validate inputs → check feature support → verify current weather → load and check settings.


1383-1384: Verify ImGuiSliderFlags_ReadOnly availability.

ImGuiSliderFlags_ReadOnly was introduced in ImGui 1.87. The explicit casts suggest awareness of potential compatibility concerns. Confirm this flag is supported by the ImGui version used in the project.

#!/bin/bash
# Check ImGui version used in the project
rg -n "IMGUI_VERSION" --type=h | head -5

# Also check if ImGuiSliderFlags_ReadOnly is defined
rg -n "ImGuiSliderFlags_ReadOnly" --type=h

1445-1456: This pattern is correct and will detect clicks properly.

ImGui::IsItemClicked() is called after ImGui::PopItemFlag() (line 1441), so the disabled flag is no longer active when the click is checked. The disabled flag controls the visual appearance during rendering (line 1438), then is properly removed before input handling. This is the intended design and works correctly.

The SliderFloat comparison is not applicable—ImGuiSliderFlags_ReadOnly is a slider-specific flag for input behavior, not a replacement for the general item flag pattern used here.

src/Feature.cpp (1)

39-40: Remove unused includes on lines 39-40.

The includes for WeatherManager.h and WeatherVariableRegistry.h are not used anywhere in this file. Remove them or add their usage to keep the code clean.

⛔ Skipped due to learnings
Learnt from: alandtse
Repo: doodlum/skyrim-community-shaders PR: 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.
Learnt from: ThePagi
Repo: doodlum/skyrim-community-shaders PR: 1369
File: src/Features/SnowCover.cpp:277-293
Timestamp: 2025-08-05T18:22:40.578Z
Learning: In the skyrim-community-shaders SnowCover feature, the wstrtostr and strtowstr utility functions defined in src/Features/SnowCover.cpp are unused and should be removed rather than fixed, as confirmed by the original author ThePagi.
src/WeatherEditor/Weather/WeatherWidget.h (1)

126-130: LGTM!

The new navigation state members and NavigateToFeatureSetting method are cleanly added. The public placement is appropriate since the method needs to be called from EditorWindow::OpenWeatherFeatureSetting.

src/WeatherManager.cpp (1)

212-228: LGTM!

The per-feature enablement gate logic is correct:

  • Defaults to false if __enabled is missing, preventing accidental activation
  • Returns early when disabled, avoiding unnecessary processing
  • Properly filters the internal __enabled flag from the output JSON
src/WeatherEditor/Weather/WeatherWidget.cpp (2)

600-628: LGTM - immediate feature settings application logic is correct.

The logic correctly:

  1. Saves settings for all features via WeatherManager
  2. Checks if the current weather matches before applying
  3. Respects the __enabled flag
  4. Filters out __enabled before applying to the registry

Using emptyWeather with lerpFactor = 1.0f provides instant feedback during editing, which is the expected UX.


1744-1746: Good UX - guidance when overrides are disabled.

The greyed-out guidance text clearly informs users that they need to enable overrides to customize settings. This prevents confusion about why controls aren't visible.

src/WeatherEditor/EditorWindow.cpp (1)

1096-1125: ImGui::SetWindowFocus is called before the window is created in the ImGui context.

At line 1121, ImGui::SetWindowFocus is invoked immediately after opening the widget. However, the widget's ImGui window is not created until DrawWidget() is called during RenderUI() (which happens later in the draw sequence). The window will not exist in ImGui's context when SetWindowFocus is executed, so the focus will not take effect until the next frame when the window has been rendered.

While the navigation to the feature/setting is set up correctly and will work when the window renders, the window focus may not be applied as intended on the same frame.

Comment on lines +1635 to +1642
if (auto* boolVar = dynamic_cast<WeatherVariables::WeatherVariable<bool>*>(var.get())) {
bool value = currentValue.get<bool>();

if (ImGui::Checkbox(varDisplayName.c_str(), &value)) {
featureJson[varName] = value;
modified = true;
}

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.

⚠️ Potential issue | 🟡 Minor

Missing exception handling for JSON type conversion.

The .get<T>() calls (lines 1636, 1657, 1681, 1703) can throw nlohmann::json::type_error if the stored value doesn't match the expected type. This could happen if settings were saved with an older version or corrupted.

Consider wrapping the value retrieval in try-catch or using value() with a default, consistent with the pattern used elsewhere (e.g., line 1544 uses .value("__enabled", false)).

Example fix for float variable
 } else if (auto* floatVar = dynamic_cast<WeatherVariables::FloatVariable*>(var.get())) {
-    float value = currentValue.get<float>();
+    float value = currentValue.is_number() ? currentValue.get<float>() : floatVar->GetMin();
     float minVal = floatVar->GetMin();
🤖 Prompt for AI Agents
In @src/WeatherEditor/Weather/WeatherWidget.cpp around lines 1635 - 1642, The
Json type conversion calls using currentValue.get<T>() in the WeatherWidget UI
code (e.g., inside the dynamic_cast to WeatherVariables::WeatherVariable<bool>
and the other branches for float/int/string) can throw
nlohmann::json::type_error; wrap those retrievals in a try/catch or replace them
with currentValue.value("<key>", <default>) to provide a safe default and avoid
exceptions, then use the retrieved safe value for
ImGui::Checkbox/Slider/TextInput and only write back to featureJson[varName]
when the UI changes (preserve modified flag behavior). Ensure you update the
bool branch (dynamic_cast<WeatherVariables::WeatherVariable<bool>>), as well as
the corresponding float/int/string branches referenced in the review, to use the
safe retrieval pattern and handle exceptions consistently.

@github-actions
Copy link
Copy Markdown

✅ A pre-release build is available for this PR:
Download

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants