diff --git a/docs/weather-system-docs/WeatherVariableRegistration.md b/docs/weather-system-docs/WeatherVariableRegistration.md index 04671712d6..09c9c2aa12 100644 --- a/docs/weather-system-docs/WeatherVariableRegistration.md +++ b/docs/weather-system-docs/WeatherVariableRegistration.md @@ -95,14 +95,49 @@ void MyFeature::RegisterWeatherVariables() override } ``` -#### Step 3: Implementation Complete +#### Step 3: Update DrawSettings() to Use Weather-Aware UI Controls + +For weather-controlled settings, use the `Util::WeatherUI` helpers instead of direct ImGui calls. This automatically greys out controls when a per-weather override is active and displays tooltips: + +```cpp +void MyFeature::DrawSettings() +{ + // Weather-aware slider (will be disabled if current weather overrides it) + Util::WeatherUI::SliderFloat("Effect Intensity", this, "Intensity", + &settings.intensity, 0.0f, 2.0f, "%.2f"); + + // Weather-aware color picker + Util::WeatherUI::ColorEdit3("Effect Color", this, "Color", + (float*)&settings.color); + + // Regular checkbox (not weather-controlled in this example) + ImGui::Checkbox("Enable Effect", (bool*)&settings.enabled); +} +``` + +**Available Weather-Aware Helpers:** + +- `Util::WeatherUI::SliderFloat()` - Float slider with min/max +- `Util::WeatherUI::Checkbox()` - Boolean checkbox +- `Util::WeatherUI::ColorEdit3()` - RGB color picker +- `Util::WeatherUI::ColorEdit4()` - RGBA color picker with alpha + +**Why Use These?** + +- Automatically detects if the current weather has overridden the setting +- Disables and greys out the control to show it's weather-controlled +- Shows tooltip: "Weather Override Active - This setting is controlled by the current weather (WeatherName)" +- Prevents confusion when editing global settings that are overridden by weather + +#### Step 4: Implementation Complete The system now automatically: - Saves/loads weather-specific settings to JSON - Interpolates variables during weather transitions -- Appears in the weather editor UI -- Handles default values and missing dataanced Usage +- Appears in the weather editor UI with per-weather toggle buttons +- Handles default values and missing data +- Shows weather-controlled status in feature settings UI ### Custom Variable Types diff --git a/src/Feature.cpp b/src/Feature.cpp index c5a2681276..89176efc9d 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -36,6 +36,8 @@ #include "Menu.h" #include "SettingsOverrideManager.h" #include "Utils/Format.h" +#include "WeatherManager.h" +#include "WeatherVariableRegistry.h" #include "State.h" @@ -362,4 +364,4 @@ bool Feature::IsFeatureKnown(const std::string& shortName, REL::Version* outVers } return false; -} \ No newline at end of file +} diff --git a/src/Utils/UI.cpp b/src/Utils/UI.cpp index f2524b9f5f..4fc410c9b1 100644 --- a/src/Utils/UI.cpp +++ b/src/Utils/UI.cpp @@ -1,8 +1,11 @@ #include "UI.h" +#include "../WeatherEditor/EditorWindow.h" #include "FileSystem.h" #include "Menu.h" #include "Menu/IconLoader.h" +#include "WeatherManager.h" +#include "WeatherVariableRegistry.h" #ifndef DIRECTINPUT_VERSION # define DIRECTINPUT_VERSION 0x0800 @@ -1329,4 +1332,250 @@ namespace Util return clicked; } + namespace WeatherUI + { + bool IsWeatherControlled(Feature* feature, const char* settingName) + { + if (!feature || !settingName) { + return false; + } + + auto* globalRegistry = WeatherVariables::GlobalWeatherRegistry::GetSingleton(); + auto* weatherManager = WeatherManager::GetSingleton(); + + // Check if this feature has registered weather variables + std::string featureName = feature->GetShortName(); + if (!globalRegistry->HasWeatherSupport(featureName)) { + return false; + } + + // Check if current weather exists + auto currentWeathers = weatherManager->GetCurrentWeathers(); + if (!currentWeathers.currentWeather) { + return false; + } + + // Load weather settings for this feature + json weatherSettings; + if (!weatherManager->LoadSettingsFromWeather(currentWeathers.currentWeather, featureName, weatherSettings)) { + return false; + } + + // Check if this specific setting has an override + return weatherSettings.contains(settingName) && !weatherSettings[settingName].is_null(); + } + + bool SliderFloat(const char* label, Feature* feature, const char* settingName, float* value, float min, float max, const char* format) + { + bool isControlled = IsWeatherControlled(feature, settingName); + + 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)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.4f, 0.4f, 0.5f, 0.9f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.5f, 0.5f, 0.6f, 1.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.7f); + } + + ImGuiSliderFlags flags = isControlled ? (static_cast(ImGuiSliderFlags_NoInput) | static_cast(ImGuiSliderFlags_ReadOnly)) : ImGuiSliderFlags_None; + bool changed = ImGui::SliderFloat(label, value, min, max, format, flags); + + if (isControlled) { + ImGui::PopStyleVar(); + ImGui::PopStyleColor(3); + + // Check if clicked + if (ImGui::IsItemClicked()) { + auto* weatherManager = WeatherManager::GetSingleton(); + auto* editorWindow = EditorWindow::GetSingleton(); + auto currentWeathers = weatherManager->GetCurrentWeathers(); + + if (currentWeathers.currentWeather && editorWindow) { + editorWindow->OpenWeatherFeatureSetting( + currentWeathers.currentWeather, + feature->GetShortName(), + settingName); + } + } + + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::BeginTooltip(); + 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(); + ImGui::EndTooltip(); + } + + return false; // Prevent changes when weather-controlled + } + + return changed; + } + + bool Checkbox(const char* label, Feature* feature, const char* settingName, bool* value) + { + bool isControlled = IsWeatherControlled(feature, settingName); + + if (isControlled) { + auto* weatherManager = WeatherManager::GetSingleton(); + auto currentWeathers = weatherManager->GetCurrentWeathers(); + + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.3f, 0.3f, 0.4f, 0.8f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.4f, 0.4f, 0.5f, 0.9f)); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.7f); + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + } + + bool changed = ImGui::Checkbox(label, value); + + if (isControlled) { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + ImGui::PopStyleColor(2); + + if (ImGui::IsItemClicked()) { + auto* weatherManager = WeatherManager::GetSingleton(); + auto* editorWindow = EditorWindow::GetSingleton(); + auto currentWeathers = weatherManager->GetCurrentWeathers(); + + if (currentWeathers.currentWeather && editorWindow) { + editorWindow->OpenWeatherFeatureSetting( + currentWeathers.currentWeather, + feature->GetShortName(), + settingName); + } + } + + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::BeginTooltip(); + 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(); + ImGui::EndTooltip(); + } + + return false; + } + + return changed; + } + + bool ColorEdit3(const char* label, Feature* feature, const char* settingName, float col[3]) + { + bool isControlled = IsWeatherControlled(feature, settingName); + + if (isControlled) { + auto* weatherManager = WeatherManager::GetSingleton(); + auto currentWeathers = weatherManager->GetCurrentWeathers(); + + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.7f); + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + } + + bool changed = ImGui::ColorEdit3(label, col); + + if (isControlled) { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + + if (ImGui::IsItemClicked()) { + auto* weatherManager = WeatherManager::GetSingleton(); + auto* editorWindow = EditorWindow::GetSingleton(); + auto currentWeathers = weatherManager->GetCurrentWeathers(); + + if (currentWeathers.currentWeather && editorWindow) { + editorWindow->OpenWeatherFeatureSetting( + currentWeathers.currentWeather, + feature->GetShortName(), + settingName); + } + } + + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::BeginTooltip(); + 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(); + ImGui::EndTooltip(); + } + + return false; + } + + return changed; + } + + bool ColorEdit4(const char* label, Feature* feature, const char* settingName, float col[4]) + { + bool isControlled = IsWeatherControlled(feature, settingName); + + if (isControlled) { + auto* weatherManager = WeatherManager::GetSingleton(); + auto currentWeathers = weatherManager->GetCurrentWeathers(); + + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.7f); + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + } + + bool changed = ImGui::ColorEdit4(label, col); + + if (isControlled) { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + + if (ImGui::IsItemClicked()) { + auto* weatherManager = WeatherManager::GetSingleton(); + auto* editorWindow = EditorWindow::GetSingleton(); + auto currentWeathers = weatherManager->GetCurrentWeathers(); + + if (currentWeathers.currentWeather && editorWindow) { + editorWindow->OpenWeatherFeatureSetting( + currentWeathers.currentWeather, + feature->GetShortName(), + settingName); + } + } + + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::BeginTooltip(); + 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(); + ImGui::EndTooltip(); + } + + return false; + } + + return changed; + } + } + } // namespace Util diff --git a/src/Utils/UI.h b/src/Utils/UI.h index dbe609df13..d2cf42bfaf 100644 --- a/src/Utils/UI.h +++ b/src/Utils/UI.h @@ -197,6 +197,46 @@ namespace Util */ float GetCenterOffsetForContent(float contentWidth); + /** + * Weather-controlled UI helpers + * These functions automatically check if a setting has a weather-specific override + * and disable the control if it's being controlled by the current weather + */ + namespace WeatherUI + { + /** + * Check if a specific setting is currently controlled by weather + * @param feature The feature to check + * @param settingName The name of the setting (must match registered weather variable name) + * @return True if weather is overriding this setting + */ + bool IsWeatherControlled(Feature* feature, const char* settingName); + + /** + * Weather-aware slider float that greys out when controlled by weather + * @param label The label for the slider + * @param feature The feature this setting belongs to + * @param settingName The name of the setting (must match registered weather variable name) + * @param value Pointer to the value + * @param min Minimum value + * @param max Maximum value + * @param format Display format + * @return True if value was changed (only possible when not weather-controlled) + */ + bool SliderFloat(const char* label, Feature* feature, const char* settingName, float* value, float min, float max, const char* format = "%.3f"); + + /** + * Weather-aware checkbox that greys out when controlled by weather + */ + bool Checkbox(const char* label, Feature* feature, const char* settingName, bool* value); + + /** + * Weather-aware color edit that greys out when controlled by weather + */ + bool ColorEdit3(const char* label, Feature* feature, const char* settingName, float col[3]); + bool ColorEdit4(const char* label, Feature* feature, const char* settingName, float col[4]); + } + /** * Draws a custom styled collapsible category header with lines extending from both sides * @param categoryName The name of the category to display diff --git a/src/WeatherEditor/EditorWindow.cpp b/src/WeatherEditor/EditorWindow.cpp index e3218876bb..15c5df6cb0 100644 --- a/src/WeatherEditor/EditorWindow.cpp +++ b/src/WeatherEditor/EditorWindow.cpp @@ -1093,6 +1093,37 @@ void EditorWindow::RenderUI() io.FontGlobalScale = previousScale; } +void EditorWindow::OpenWeatherFeatureSetting(RE::TESWeather* weather, const std::string& featureName, const std::string& settingName) +{ + if (!weather) { + return; + } + + // Open the editor if it's not already open + if (!open) { + open = true; + } + + // Find the weather widget + for (auto& widget : weatherWidgets) { + auto* weatherWidget = dynamic_cast(widget.get()); + if (weatherWidget && weatherWidget->weather == weather) { + // Open the widget if it's not already open + if (!weatherWidget->open) { + weatherWidget->open = true; + } + + // Set up navigation to the specific feature/setting + weatherWidget->NavigateToFeatureSetting(featureName, settingName); + + // Focus the widget window + std::string windowName = std::format("{}###widget_{}", weatherWidget->GetEditorID(), (void*)weatherWidget); + ImGui::SetWindowFocus(windowName.c_str()); + break; + } + } +} + EditorWindow::~EditorWindow() { delete tempTexture; diff --git a/src/WeatherEditor/EditorWindow.h b/src/WeatherEditor/EditorWindow.h index 17c73df807..98959811b8 100644 --- a/src/WeatherEditor/EditorWindow.h +++ b/src/WeatherEditor/EditorWindow.h @@ -161,6 +161,9 @@ class EditorWindow void SaveSessionWidgets(); void RestoreSessionWidgets(); + // Navigation helpers for weather-controlled settings + void OpenWeatherFeatureSetting(RE::TESWeather* weather, const std::string& featureName, const std::string& settingName); + ~EditorWindow(); private: diff --git a/src/WeatherEditor/Weather/WeatherWidget.cpp b/src/WeatherEditor/Weather/WeatherWidget.cpp index 645b4f166b..9350a88169 100644 --- a/src/WeatherEditor/Weather/WeatherWidget.cpp +++ b/src/WeatherEditor/Weather/WeatherWidget.cpp @@ -597,6 +597,35 @@ void WeatherWidget::SetWeatherValues() } } weather->cloudLayerDisabledBits = disabledBits; + + // Save feature settings + auto* weatherManager = WeatherManager::GetSingleton(); + for (const auto& [featureName, featureSettings] : settings.featureSettings) { + weatherManager->SaveSettingsToWeather(weather, featureName, featureSettings); + } + + // If this weather is currently active, immediately apply feature settings + auto currentWeathers = weatherManager->GetCurrentWeathers(); + if (currentWeathers.currentWeather == weather) { + auto* globalRegistry = WeatherVariables::GlobalWeatherRegistry::GetSingleton(); + for (const auto& [featureName, featureSettings] : settings.featureSettings) { + // Check if overrides are enabled for this feature + bool enabled = featureSettings.value("__enabled", false); + if (enabled && globalRegistry->HasWeatherSupport(featureName)) { + // Filter out the __enabled flag before applying + json filteredSettings = json::object(); + for (auto it = featureSettings.begin(); it != featureSettings.end(); ++it) { + if (it.key() != "__enabled") { + filteredSettings[it.key()] = it.value(); + } + } + + // Apply the weather-specific settings immediately + json emptyWeather; // No previous weather during instant update + globalRegistry->UpdateFeatureFromWeathers(featureName, emptyWeather, filteredSettings, 1.0f); + } + } + } } void WeatherWidget::LoadWeatherValues() @@ -1476,7 +1505,9 @@ void WeatherWidget::RevertChanges() void WeatherWidget::DrawFeatureSettings() { - ImGui::TextWrapped("Configure feature-specific settings that will be applied when this weather is active."); + ImGui::TextWrapped( + "Configure feature-specific settings that will be applied when this weather is active. " + "These override the feature's global settings for this weather only."); ImGui::Spacing(); auto* globalRegistry = WeatherVariables::GlobalWeatherRegistry::GetSingleton(); @@ -1487,52 +1518,252 @@ void WeatherWidget::DrawFeatureSettings() } std::string featureName = feature->GetShortName(); + auto* featureRegistry = globalRegistry->GetFeatureRegistry(featureName); // Check if feature has registered weather variables - if (!globalRegistry->HasWeatherSupport(featureName)) { + if (!featureRegistry) { continue; } std::string displayName = feature->GetName(); + // Get or initialize feature settings for this weather + if (settings.featureSettings.find(featureName) == settings.featureSettings.end()) { + settings.featureSettings[featureName] = json::object(); + } + auto& featureJson = settings.featureSettings[featureName]; + + // Handle pending navigation - auto-expand this feature if it matches + bool shouldAutoExpand = (pendingFeatureNavigation == featureName); + if (shouldAutoExpand) { + ImGui::SetNextItemOpen(true); + } + if (ImGui::TreeNode(displayName.c_str())) { - ImGui::Text("Feature: %s", featureName.c_str()); - ImGui::Spacing(); + // Check if weather-specific overrides are enabled (using special key) + bool overridesEnabled = featureJson.value("__enabled", false); - // Show if settings exist for this feature - bool hasSettings = settings.featureSettings.find(featureName) != settings.featureSettings.end() && - !settings.featureSettings[featureName].empty(); + // Weather-specific override toggle + ImGui::PushStyleColor(ImGuiCol_Button, overridesEnabled ? ImVec4(0.2f, 0.7f, 0.2f, 1.0f) : ImVec4(0.5f, 0.5f, 0.5f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, overridesEnabled ? ImVec4(0.3f, 0.8f, 0.3f, 1.0f) : ImVec4(0.6f, 0.6f, 0.6f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, overridesEnabled ? ImVec4(0.1f, 0.6f, 0.1f, 1.0f) : ImVec4(0.4f, 0.4f, 0.4f, 1.0f)); - if (hasSettings) { - ImGui::TextColored({ 0.0f, 1.0f, 0.0f, 1.0f }, "Has weather-specific settings"); + bool toggleClicked = ImGui::Button(overridesEnabled ? "Using Weather-Specific Settings" : "Using Global Settings", ImVec2(-1, 0)); - if (Util::ButtonWithFlash("Clear Settings")) { - settings.featureSettings[featureName] = json::object(); - } - ImGui::SameLine(); - if (Util::ButtonWithFlash("View JSON")) { - ImGui::OpenPopup("FeatureJSON"); + ImGui::PopStyleColor(3); + + if (auto _tt = Util::HoverTooltipWrapper()) { + if (overridesEnabled) { + ImGui::Text("This weather has custom overrides for this feature."); + ImGui::Text("Click to disable overrides and use global settings instead."); + ImGui::Text("(Settings will be preserved but not applied)"); + } else { + ImGui::Text("This weather uses global feature settings."); + ImGui::Text("Click to enable weather-specific overrides."); } + } - if (ImGui::BeginPopup("FeatureJSON")) { - ImGui::Text("Settings JSON:"); - ImGui::Separator(); - std::string jsonStr = settings.featureSettings[featureName].dump(2); - ImGui::TextWrapped("%s", jsonStr.c_str()); - ImGui::EndPopup(); + if (toggleClicked) { + if (overridesEnabled) { + // Disable overrides - mark as disabled but keep the settings + featureJson["__enabled"] = false; + } else { + // Enable overrides - mark as enabled + featureJson["__enabled"] = true; + // If no settings exist yet, copy current global values as starting point + bool hasActualSettings = false; + for (auto it = featureJson.begin(); it != featureJson.end(); ++it) { + if (it.key() != "__enabled") { + hasActualSettings = true; + break; + } + } + if (!hasActualSettings) { + const auto& variables = featureRegistry->GetVariables(); + for (const auto& var : variables) { + json tempJson; + var->SaveToJson(tempJson); + std::string varName = var->GetName(); + if (tempJson.contains(varName)) { + featureJson[varName] = tempJson[varName]; + } + } + } + } + EditorWindow::GetSingleton()->PushUndoState(this); + if (EditorWindow::GetSingleton()->settings.autoApplyChanges) { + ApplyChanges(); } - } else { - ImGui::TextColored({ 0.7f, 0.7f, 0.7f, 1.0f }, "No weather-specific settings"); } ImGui::Spacing(); - ImGui::TextWrapped( - "Note: Feature settings should be configured through the feature's own settings panel. " - "This section shows which features have per-weather overrides."); + ImGui::Separator(); + ImGui::Spacing(); + + // Only show controls if weather-specific overrides are enabled + if (overridesEnabled) { + // Draw UI for each registered variable + const auto& variables = featureRegistry->GetVariables(); + bool modified = false; + + for (const auto& var : variables) { + std::string varName = var->GetName(); + std::string varDisplayName = var->GetDisplayName(); + std::string tooltip = var->GetTooltip(); + + ImGui::PushID(varName.c_str()); + + // Check if this variable has a weather-specific value + bool hasOverride = featureJson.contains(varName); + + // Get the current value + // If we have an override, use it; otherwise get from feature's live value + if (!hasOverride) { + // Initialize from feature's current value + json tempJson; + var->SaveToJson(tempJson); + if (tempJson.contains(varName)) { + featureJson[varName] = tempJson[varName]; + hasOverride = true; // Now we have a value to work with + } + } + + json currentValue = featureJson[varName]; + + // Try to detect variable type and render appropriate control + // Check if it's a bool variable first + if (auto* boolVar = dynamic_cast*>(var.get())) { + bool value = currentValue.get(); + + if (ImGui::Checkbox(varDisplayName.c_str(), &value)) { + featureJson[varName] = value; + modified = true; + } + + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("%s", tooltip.c_str()); + } + + // Right-click context menu to reset individual values + if (ImGui::BeginPopupContextItem()) { + if (ImGui::MenuItem("Reset to Global")) { + featureJson.erase(varName); + modified = true; + } + ImGui::EndPopup(); + } + + } else if (auto* floatVar = dynamic_cast(var.get())) { + float value = currentValue.get(); + float minVal = floatVar->GetMin(); + float maxVal = floatVar->GetMax(); + + if (ImGui::SliderFloat(varDisplayName.c_str(), &value, minVal, maxVal, "%.3f")) { + featureJson[varName] = value; + modified = true; + } + + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("%s", tooltip.c_str()); + } + + // Right-click context menu to reset individual values + if (ImGui::BeginPopupContextItem()) { + if (ImGui::MenuItem("Reset to Global")) { + featureJson.erase(varName); + modified = true; + } + ImGui::EndPopup(); + } + + } else if (auto* float3Var = dynamic_cast(var.get())) { + // Handle float3 (color) variables + float3 value = currentValue.get(); + float colorArray[3] = { value.x, value.y, value.z }; + + if (ImGui::ColorEdit3(varDisplayName.c_str(), colorArray)) { + featureJson[varName] = json{ colorArray[0], colorArray[1], colorArray[2] }; + modified = true; + } + + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("%s", tooltip.c_str()); + } + + if (ImGui::BeginPopupContextItem()) { + if (ImGui::MenuItem("Reset to Global")) { + featureJson.erase(varName); + modified = true; + } + ImGui::EndPopup(); + } + + } else if (auto* float4Var = dynamic_cast(var.get())) { + // Handle float4 (color with alpha) variables + float4 value = currentValue.get(); + float colorArray[4] = { value.x, value.y, value.z, value.w }; + + if (ImGui::ColorEdit4(varDisplayName.c_str(), colorArray)) { + featureJson[varName] = json{ colorArray[0], colorArray[1], colorArray[2], colorArray[3] }; + modified = true; + } + + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::Text("%s", tooltip.c_str()); + } + + if (ImGui::BeginPopupContextItem()) { + if (ImGui::MenuItem("Reset to Global")) { + featureJson.erase(varName); + modified = true; + } + ImGui::EndPopup(); + } + + } else { + // Generic handling for other types + ImGui::TextDisabled("%s: %s", varDisplayName.c_str(), currentValue.dump().c_str()); + if (auto _tt = Util::HoverTooltipWrapper()) { + ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "Unsupported Variable Type"); + ImGui::Text("%s", tooltip.c_str()); + ImGui::Separator(); + ImGui::TextWrapped("This variable type doesn't have a custom UI implementation yet. The raw JSON value is shown above."); + } + } + + ImGui::PopID(); + } + + if (modified) { + EditorWindow::GetSingleton()->PushUndoState(this); + if (EditorWindow::GetSingleton()->settings.autoApplyChanges) { + ApplyChanges(); + } + } + + } else { + ImGui::TextColored({ 0.7f, 0.7f, 0.7f, 1.0f }, "Enable weather-specific overrides above to customize settings for this weather."); + } ImGui::TreePop(); } } + + // Clear navigation state after processing + if (!pendingFeatureNavigation.empty()) { + pendingFeatureNavigation.clear(); + pendingSettingHighlight.clear(); + } +} + +void WeatherWidget::NavigateToFeatureSetting(const std::string& featureName, const std::string& settingName) +{ + // Store the navigation request + pendingFeatureNavigation = featureName; + pendingSettingHighlight = settingName; + + // Switch to Features tab + activeTabOverride = "Features"; } void WeatherWidget::UpdateSearchResults() diff --git a/src/WeatherEditor/Weather/WeatherWidget.h b/src/WeatherEditor/Weather/WeatherWidget.h index cf3f0f5403..84c696cbd4 100644 --- a/src/WeatherEditor/Weather/WeatherWidget.h +++ b/src/WeatherEditor/Weather/WeatherWidget.h @@ -123,6 +123,11 @@ class WeatherWidget : public Widget void SaveFeatureSettings(); void LoadFeatureSettings(); + // Navigation state for opening specific features + std::string pendingFeatureNavigation = ""; + std::string pendingSettingHighlight = ""; + void NavigateToFeatureSetting(const std::string& featureName, const std::string& settingName); + private: void DrawDALCSettings(); void DrawWeatherColorSettings(); diff --git a/src/WeatherManager.cpp b/src/WeatherManager.cpp index 2dd2d14e8f..41f3ec39b1 100644 --- a/src/WeatherManager.cpp +++ b/src/WeatherManager.cpp @@ -209,7 +209,22 @@ bool WeatherManager::LoadSettingsFromWeather(RE::TESWeather* weather, const std: if (weatherIt != perWeatherSettingsCache.end()) { auto featureIt = weatherIt->second.find(featureName); if (featureIt != weatherIt->second.end()) { - o_json = featureIt->second; + const json& featureJson = featureIt->second; + + // Check if weather-specific overrides are enabled + bool enabled = featureJson.value("__enabled", false); + if (!enabled) { + // Settings exist but are disabled, return empty + return false; + } + + // Copy all settings except the __enabled flag + o_json = json::object(); + for (auto it = featureJson.begin(); it != featureJson.end(); ++it) { + if (it.key() != "__enabled") { + o_json[it.key()] = it.value(); + } + } return true; } }